duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

achievementsettingswidget.cpp (11673B)


      1 // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "achievementsettingswidget.h"
      5 #include "achievementlogindialog.h"
      6 #include "mainwindow.h"
      7 #include "qtutils.h"
      8 #include "settingswindow.h"
      9 #include "settingwidgetbinder.h"
     10 
     11 #include "core/achievements.h"
     12 #include "core/system.h"
     13 
     14 #include "common/string_util.h"
     15 
     16 #include <QtCore/QDateTime>
     17 #include <QtWidgets/QMessageBox>
     18 
     19 AchievementSettingsWidget::AchievementSettingsWidget(SettingsWindow* dialog, QWidget* parent)
     20   : QWidget(parent), m_dialog(dialog)
     21 {
     22   SettingsInterface* sif = dialog->getSettingsInterface();
     23 
     24   m_ui.setupUi(this);
     25 
     26   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enable, "Cheevos", "Enabled", false);
     27   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hardcoreMode, "Cheevos", "ChallengeMode", false);
     28   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.achievementNotifications, "Cheevos", "Notifications", true);
     29   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.leaderboardNotifications, "Cheevos",
     30                                                "LeaderboardNotifications", true);
     31   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.soundEffects, "Cheevos", "SoundEffects", true);
     32   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.overlays, "Cheevos", "Overlays", true);
     33   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.encoreMode, "Cheevos", "EncoreMode", false);
     34   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.spectatorMode, "Cheevos", "SpectatorMode", false);
     35   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.unofficialAchievements, "Cheevos", "UnofficialTestMode",
     36                                                false);
     37   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useFirstDiscFromPlaylist, "Cheevos",
     38                                                "UseFirstDiscFromPlaylist", true);
     39   SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.achievementNotificationsDuration, "Cheevos",
     40                                                 "NotificationsDuration",
     41                                                 Settings::DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME);
     42   SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.leaderboardNotificationsDuration, "Cheevos",
     43                                                 "LeaderboardsDuration",
     44                                                 Settings::DEFAULT_LEADERBOARD_NOTIFICATION_TIME);
     45 
     46   dialog->registerWidgetHelp(m_ui.enable, tr("Enable Achievements"), tr("Unchecked"),
     47                              tr("When enabled and logged in, DuckStation will scan for achievements on startup."));
     48   dialog->registerWidgetHelp(m_ui.hardcoreMode, tr("Enable Hardcore Mode"), tr("Unchecked"),
     49                              tr("\"Challenge\" mode for achievements, including leaderboard tracking. Disables save "
     50                                 "state, cheats, and slowdown functions."));
     51   dialog->registerWidgetHelp(m_ui.achievementNotifications, tr("Show Achievement Notifications"), tr("Checked"),
     52                              tr("Displays popup messages on events such as achievement unlocks and game completion."));
     53   dialog->registerWidgetHelp(
     54     m_ui.leaderboardNotifications, tr("Show Leaderboard Notifications"), tr("Checked"),
     55     tr("Displays popup messages when starting, submitting, or failing a leaderboard challenge."));
     56   dialog->registerWidgetHelp(
     57     m_ui.soundEffects, tr("Enable Sound Effects"), tr("Checked"),
     58     tr("Plays sound effects for events such as achievement unlocks and leaderboard submissions."));
     59   dialog->registerWidgetHelp(
     60     m_ui.overlays, tr("Enable In-Game Overlays"), tr("Checked"),
     61     tr("Shows icons in the lower-right corner of the screen when a challenge/primed achievement is active."));
     62   dialog->registerWidgetHelp(m_ui.encoreMode, tr("Enable Encore Mode"), tr("Unchecked"),
     63                              tr("When enabled, each session will behave as if no achievements have been unlocked."));
     64   dialog->registerWidgetHelp(m_ui.spectatorMode, tr("Enable Spectator Mode"), tr("Unchecked"),
     65                              tr("When enabled, DuckStation will assume all achievements are locked and not send any "
     66                                 "unlock notifications to the server."));
     67   dialog->registerWidgetHelp(
     68     m_ui.unofficialAchievements, tr("Test Unofficial Achievements"), tr("Unchecked"),
     69     tr("When enabled, DuckStation will list achievements from unofficial sets. Please note that these achievements are "
     70        "not tracked by RetroAchievements, so they unlock every time."));
     71   dialog->registerWidgetHelp(
     72     m_ui.useFirstDiscFromPlaylist, tr("Use First Disc From Playlist"), tr("Unchecked"),
     73     tr(
     74       "When enabled, the first disc in a playlist will be used for achievements, regardless of which disc is active."));
     75 
     76   connect(m_ui.enable, &QCheckBox::checkStateChanged, this, &AchievementSettingsWidget::updateEnableState);
     77   connect(m_ui.hardcoreMode, &QCheckBox::checkStateChanged, this, &AchievementSettingsWidget::updateEnableState);
     78   connect(m_ui.hardcoreMode, &QCheckBox::checkStateChanged, this, &AchievementSettingsWidget::onHardcoreModeStateChanged);
     79   connect(m_ui.achievementNotifications, &QCheckBox::checkStateChanged, this, &AchievementSettingsWidget::updateEnableState);
     80   connect(m_ui.leaderboardNotifications, &QCheckBox::checkStateChanged, this, &AchievementSettingsWidget::updateEnableState);
     81   connect(m_ui.achievementNotificationsDuration, &QSlider::valueChanged, this,
     82           &AchievementSettingsWidget::onAchievementsNotificationDurationSliderChanged);
     83   connect(m_ui.leaderboardNotificationsDuration, &QSlider::valueChanged, this,
     84           &AchievementSettingsWidget::onLeaderboardsNotificationDurationSliderChanged);
     85 
     86   if (!m_dialog->isPerGameSettings())
     87   {
     88     connect(m_ui.loginButton, &QPushButton::clicked, this, &AchievementSettingsWidget::onLoginLogoutPressed);
     89     connect(m_ui.viewProfile, &QPushButton::clicked, this, &AchievementSettingsWidget::onViewProfilePressed);
     90     connect(g_emu_thread, &EmuThread::achievementsRefreshed, this, &AchievementSettingsWidget::onAchievementsRefreshed);
     91     updateLoginState();
     92 
     93     // force a refresh of game info
     94     Host::RunOnCPUThread(Host::OnAchievementsRefreshed);
     95   }
     96   else
     97   {
     98     // remove login and game info, not relevant for per-game
     99     m_ui.verticalLayout->removeWidget(m_ui.gameInfoBox);
    100     m_ui.gameInfoBox->deleteLater();
    101     m_ui.gameInfoBox = nullptr;
    102     m_ui.verticalLayout->removeWidget(m_ui.loginBox);
    103     m_ui.loginBox->deleteLater();
    104     m_ui.loginBox = nullptr;
    105   }
    106 
    107   updateEnableState();
    108   onAchievementsNotificationDurationSliderChanged();
    109   onLeaderboardsNotificationDurationSliderChanged();
    110 }
    111 
    112 AchievementSettingsWidget::~AchievementSettingsWidget() = default;
    113 
    114 void AchievementSettingsWidget::updateEnableState()
    115 {
    116   const bool enabled = m_dialog->getEffectiveBoolValue("Cheevos", "Enabled", false);
    117   const bool notifications = enabled && m_dialog->getEffectiveBoolValue("Cheevos", "Notifications", true);
    118   const bool lb_notifications = enabled && m_dialog->getEffectiveBoolValue("Cheevos", "LeaderboardNotifications", true);
    119   m_ui.hardcoreMode->setEnabled(enabled);
    120   m_ui.achievementNotifications->setEnabled(enabled);
    121   m_ui.leaderboardNotifications->setEnabled(enabled);
    122   m_ui.achievementNotificationsDuration->setEnabled(notifications);
    123   m_ui.achievementNotificationsDurationLabel->setEnabled(notifications);
    124   m_ui.leaderboardNotificationsDuration->setEnabled(lb_notifications);
    125   m_ui.leaderboardNotificationsDurationLabel->setEnabled(lb_notifications);
    126   m_ui.soundEffects->setEnabled(enabled);
    127   m_ui.overlays->setEnabled(enabled);
    128   m_ui.encoreMode->setEnabled(enabled);
    129   m_ui.spectatorMode->setEnabled(enabled);
    130   m_ui.unofficialAchievements->setEnabled(enabled);
    131   m_ui.useFirstDiscFromPlaylist->setEnabled(enabled);
    132 }
    133 
    134 void AchievementSettingsWidget::onHardcoreModeStateChanged()
    135 {
    136   if (!QtHost::IsSystemValid())
    137     return;
    138 
    139   const bool enabled = m_dialog->getEffectiveBoolValue("Cheevos", "Enabled", false);
    140   const bool challenge = m_dialog->getEffectiveBoolValue("Cheevos", "ChallengeMode", false);
    141   if (!enabled || !challenge)
    142     return;
    143 
    144   // don't bother prompting if the game doesn't have achievements
    145   auto lock = Achievements::GetLock();
    146   if (!Achievements::HasActiveGame())
    147     return;
    148 
    149   if (QMessageBox::question(
    150         QtUtils::GetRootWidget(this), tr("Reset System"),
    151         tr("Hardcore mode will not be enabled until the system is reset. Do you want to reset the system now?")) !=
    152       QMessageBox::Yes)
    153   {
    154     return;
    155   }
    156 
    157   g_emu_thread->resetSystem(true);
    158 }
    159 
    160 void AchievementSettingsWidget::onAchievementsNotificationDurationSliderChanged()
    161 {
    162   const int duration =
    163     m_dialog->getEffectiveIntValue("Cheevos", "NotificationsDuration", Settings::DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME);
    164   m_ui.achievementNotificationsDurationLabel->setText(tr("%n seconds", nullptr, duration));
    165 }
    166 
    167 void AchievementSettingsWidget::onLeaderboardsNotificationDurationSliderChanged()
    168 {
    169   const int duration =
    170     m_dialog->getEffectiveIntValue("Cheevos", "LeaderboardsDuration", Settings::DEFAULT_LEADERBOARD_NOTIFICATION_TIME);
    171   m_ui.leaderboardNotificationsDurationLabel->setText(tr("%n seconds", nullptr, duration));
    172 }
    173 
    174 void AchievementSettingsWidget::updateLoginState()
    175 {
    176   const std::string username(Host::GetBaseStringSettingValue("Cheevos", "Username"));
    177   const bool logged_in = !username.empty();
    178 
    179   if (logged_in)
    180   {
    181     const u64 login_unix_timestamp =
    182       StringUtil::FromChars<u64>(Host::GetBaseStringSettingValue("Cheevos", "LoginTimestamp", "0")).value_or(0);
    183     const QDateTime login_timestamp(QDateTime::fromSecsSinceEpoch(static_cast<qint64>(login_unix_timestamp)));
    184     m_ui.loginStatus->setText(tr("Username: %1\nLogin token generated on %2.")
    185                                 .arg(QString::fromStdString(username))
    186                                 .arg(login_timestamp.toString(Qt::TextDate)));
    187     m_ui.loginButton->setText(tr("Logout"));
    188   }
    189   else
    190   {
    191     m_ui.loginStatus->setText(tr("Not Logged In."));
    192     m_ui.loginButton->setText(tr("Login..."));
    193   }
    194 
    195   m_ui.viewProfile->setEnabled(logged_in);
    196 }
    197 
    198 void AchievementSettingsWidget::onLoginLogoutPressed()
    199 {
    200   if (!Host::GetBaseStringSettingValue("Cheevos", "Username").empty())
    201   {
    202     Host::RunOnCPUThread([]() { Achievements::Logout(); }, true);
    203     updateLoginState();
    204     return;
    205   }
    206 
    207   AchievementLoginDialog login(this, Achievements::LoginRequestReason::UserInitiated);
    208   int res = login.exec();
    209   if (res != 0)
    210     return;
    211 
    212   updateLoginState();
    213 
    214   // Login can enable achievements/hardcore.
    215   if (!m_ui.enable->isChecked() && Host::GetBaseBoolSettingValue("Cheevos", "Enabled", false))
    216   {
    217     QSignalBlocker sb(m_ui.enable);
    218     m_ui.enable->setChecked(true);
    219     updateEnableState();
    220   }
    221   if (!m_ui.hardcoreMode->isChecked() && Host::GetBaseBoolSettingValue("Cheevos", "ChallengeMode", false))
    222   {
    223     QSignalBlocker sb(m_ui.hardcoreMode);
    224     m_ui.hardcoreMode->setChecked(true);
    225   }
    226 }
    227 
    228 void AchievementSettingsWidget::onViewProfilePressed()
    229 {
    230   const std::string username(Host::GetBaseStringSettingValue("Cheevos", "Username"));
    231   if (username.empty())
    232     return;
    233 
    234   const QByteArray encoded_username(QUrl::toPercentEncoding(QString::fromStdString(username)));
    235   QtUtils::OpenURL(
    236     QtUtils::GetRootWidget(this),
    237     QUrl(QStringLiteral("https://retroachievements.org/user/%1").arg(QString::fromUtf8(encoded_username))));
    238 }
    239 
    240 void AchievementSettingsWidget::onAchievementsRefreshed(quint32 id, const QString& game_info_string)
    241 {
    242   m_ui.gameInfo->setText(game_info_string);
    243 }