2024-07-30 11:42:36 +00:00
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
2022-04-18 13:35:14 +00:00
# include "AchievementSettingsWidget.h"
# include "AchievementLoginDialog.h"
# include "MainWindow.h"
2023-10-14 08:45:09 +00:00
# include "SettingsWindow.h"
2022-04-18 13:35:14 +00:00
# include "SettingWidgetBinder.h"
# include "QtUtils.h"
2023-05-13 04:30:41 +00:00
# include "pcsx2/Achievements.h"
# include "pcsx2/Host.h"
2022-04-18 13:35:14 +00:00
# include "common/StringUtil.h"
# include <QtCore/QDateTime>
# include <QtWidgets/QMessageBox>
2023-10-14 08:45:09 +00:00
AchievementSettingsWidget : : AchievementSettingsWidget ( SettingsWindow * dialog , QWidget * parent )
2022-04-18 13:35:14 +00:00
: QWidget ( parent )
, m_dialog ( dialog )
{
SettingsInterface * sif = dialog - > getSettingsInterface ( ) ;
2023-09-17 10:19:51 +00:00
m_ui . setupUi ( this ) ;
2022-04-18 13:35:14 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . enable , " Achievements " , " Enabled " , false ) ;
2023-09-17 10:19:51 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . hardcoreMode , " Achievements " , " ChallengeMode " , false ) ;
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . achievementNotifications , " Achievements " , " Notifications " , true ) ;
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . leaderboardNotifications , " Achievements " , " LeaderboardNotifications " , true ) ;
2022-12-06 14:46:26 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . soundEffects , " Achievements " , " SoundEffects " , true ) ;
2023-09-17 10:19:51 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . overlays , " Achievements " , " Overlays " , true ) ;
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . encoreMode , " Achievements " , " EncoreMode " , false ) ;
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . spectatorMode , " Achievements " , " SpectatorMode " , false ) ;
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . unofficialAchievements , " Achievements " , " UnofficialTestMode " , false ) ;
SettingWidgetBinder : : BindWidgetToIntSetting ( sif , m_ui . achievementNotificationsDuration , " Achievements " , " NotificationsDuration " , Pcsx2Config : : AchievementsOptions : : DEFAULT_NOTIFICATION_DURATION ) ;
SettingWidgetBinder : : BindWidgetToIntSetting ( sif , m_ui . leaderboardNotificationsDuration , " Achievements " , " LeaderboardsDuration " , Pcsx2Config : : AchievementsOptions : : DEFAULT_LEADERBOARD_DURATION ) ;
dialog - > registerWidgetHelp ( m_ui . enable , tr ( " Enable Achievements " ) , tr ( " Unchecked " ) , tr ( " When enabled and logged in, PCSX2 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 " ) , tr ( " When enabled, PCSX2 will assume all achievements are locked and not send any unlock notifications to the server. " ) ) ;
dialog - > registerWidgetHelp ( m_ui . unofficialAchievements , tr ( " Test Unofficial Achievements " ) , tr ( " Unchecked " ) , tr ( " When enabled, PCSX2 will list achievements from unofficial sets. Please note that these achievements are not tracked by RetroAchievements, so they unlock every time. " ) ) ;
2023-08-05 15:39:04 +00:00
2024-04-12 12:22:58 +00:00
connect ( m_ui . enable , & QCheckBox : : checkStateChanged , this , & AchievementSettingsWidget : : updateEnableState ) ;
connect ( m_ui . hardcoreMode , & QCheckBox : : checkStateChanged , this , & AchievementSettingsWidget : : updateEnableState ) ;
connect ( m_ui . hardcoreMode , & QCheckBox : : checkStateChanged , this , & AchievementSettingsWidget : : onHardcoreModeStateChanged ) ;
connect ( m_ui . achievementNotifications , & QCheckBox : : checkStateChanged , this , & AchievementSettingsWidget : : updateEnableState ) ;
connect ( m_ui . leaderboardNotifications , & QCheckBox : : checkStateChanged , this , & AchievementSettingsWidget : : updateEnableState ) ;
2023-09-17 10:19:51 +00:00
connect ( m_ui . achievementNotificationsDuration , & QSlider : : valueChanged , this , & AchievementSettingsWidget : : onAchievementsNotificationDurationSliderChanged ) ;
connect ( m_ui . leaderboardNotificationsDuration , & QSlider : : valueChanged , this , & AchievementSettingsWidget : : onLeaderboardsNotificationDurationSliderChanged ) ;
2022-04-18 13:35:14 +00:00
if ( ! m_dialog - > isPerGameSettings ( ) )
{
connect ( m_ui . loginButton , & QPushButton : : clicked , this , & AchievementSettingsWidget : : onLoginLogoutPressed ) ;
connect ( m_ui . viewProfile , & QPushButton : : clicked , this , & AchievementSettingsWidget : : onViewProfilePressed ) ;
connect ( g_emu_thread , & EmuThread : : onAchievementsRefreshed , this , & AchievementSettingsWidget : : onAchievementsRefreshed ) ;
updateLoginState ( ) ;
// force a refresh of game info
Host : : RunOnCPUThread ( Host : : OnAchievementsRefreshed ) ;
}
else
{
// remove login and game info, not relevant for per-game
m_ui . verticalLayout - > removeWidget ( m_ui . gameInfoBox ) ;
m_ui . gameInfoBox - > deleteLater ( ) ;
m_ui . gameInfoBox = nullptr ;
m_ui . verticalLayout - > removeWidget ( m_ui . loginBox ) ;
m_ui . loginBox - > deleteLater ( ) ;
m_ui . loginBox = nullptr ;
}
updateEnableState ( ) ;
2023-09-17 10:19:51 +00:00
onAchievementsNotificationDurationSliderChanged ( ) ;
onLeaderboardsNotificationDurationSliderChanged ( ) ;
2022-04-18 13:35:14 +00:00
}
AchievementSettingsWidget : : ~ AchievementSettingsWidget ( ) = default ;
void AchievementSettingsWidget : : updateEnableState ( )
{
const bool enabled = m_dialog - > getEffectiveBoolValue ( " Achievements " , " Enabled " , false ) ;
2023-09-17 10:19:51 +00:00
const bool notifications = enabled & & m_dialog - > getEffectiveBoolValue ( " Achievements " , " Notifications " , true ) ;
const bool lb_notifications = enabled & & m_dialog - > getEffectiveBoolValue ( " Achievements " , " LeaderboardNotifications " , true ) ;
m_ui . hardcoreMode - > setEnabled ( enabled ) ;
m_ui . achievementNotifications - > setEnabled ( enabled ) ;
m_ui . leaderboardNotifications - > setEnabled ( enabled ) ;
m_ui . achievementNotificationsDuration - > setEnabled ( notifications ) ;
m_ui . achievementNotificationsDurationLabel - > setEnabled ( notifications ) ;
m_ui . leaderboardNotificationsDuration - > setEnabled ( lb_notifications ) ;
m_ui . leaderboardNotificationsDurationLabel - > setEnabled ( lb_notifications ) ;
2023-01-28 03:57:41 +00:00
m_ui . soundEffects - > setEnabled ( enabled ) ;
2023-09-17 10:19:51 +00:00
m_ui . overlays - > setEnabled ( enabled ) ;
m_ui . encoreMode - > setEnabled ( enabled ) ;
m_ui . spectatorMode - > setEnabled ( enabled ) ;
m_ui . unofficialAchievements - > setEnabled ( enabled ) ;
2022-04-18 13:35:14 +00:00
}
2023-09-17 10:19:51 +00:00
void AchievementSettingsWidget : : onHardcoreModeStateChanged ( )
2022-04-18 13:35:14 +00:00
{
if ( ! QtHost : : IsVMValid ( ) )
return ;
const bool enabled = m_dialog - > getEffectiveBoolValue ( " Achievements " , " Enabled " , false ) ;
const bool challenge = m_dialog - > getEffectiveBoolValue ( " Achievements " , " ChallengeMode " , false ) ;
if ( ! enabled | | ! challenge )
return ;
// don't bother prompting if the game doesn't have achievements
auto lock = Achievements : : GetLock ( ) ;
2023-10-24 08:18:43 +00:00
if ( ! Achievements : : HasActiveGame ( ) | | ! Achievements : : HasAchievementsOrLeaderboards ( ) )
2022-04-18 13:35:14 +00:00
return ;
2023-09-17 10:19:51 +00:00
if ( QMessageBox : : question (
QtUtils : : GetRootWidget ( this ) , tr ( " Reset System " ) ,
tr ( " Hardcore mode will not be enabled until the system is reset. Do you want to reset the system now? " ) ) ! =
QMessageBox : : Yes )
2022-04-18 13:35:14 +00:00
{
return ;
}
g_emu_thread - > resetVM ( ) ;
}
2023-09-17 10:19:51 +00:00
void AchievementSettingsWidget : : onAchievementsNotificationDurationSliderChanged ( )
{
const float duration = m_dialog - > getEffectiveFloatValue ( " Achievements " , " NotificationsDuration " ,
Pcsx2Config : : AchievementsOptions : : DEFAULT_NOTIFICATION_DURATION ) ;
m_ui . achievementNotificationsDurationLabel - > setText ( tr ( " %n seconds " , nullptr , static_cast < int > ( duration ) ) ) ;
}
void AchievementSettingsWidget : : onLeaderboardsNotificationDurationSliderChanged ( )
{
const float duration = m_dialog - > getEffectiveFloatValue ( " Achievements " , " LeaderboardsDuration " ,
Pcsx2Config : : AchievementsOptions : : DEFAULT_LEADERBOARD_DURATION ) ;
m_ui . leaderboardNotificationsDurationLabel - > setText ( tr ( " %n seconds " , nullptr , static_cast < int > ( duration ) ) ) ;
}
2022-04-18 13:35:14 +00:00
void AchievementSettingsWidget : : updateLoginState ( )
{
const std : : string username ( Host : : GetBaseStringSettingValue ( " Achievements " , " Username " ) ) ;
const bool logged_in = ! username . empty ( ) ;
if ( logged_in )
{
2022-12-06 14:46:26 +00:00
const u64 login_unix_timestamp =
StringUtil : : FromChars < u64 > ( Host : : GetBaseStringSettingValue ( " Achievements " , " LoginTimestamp " , " 0 " ) ) . value_or ( 0 ) ;
2022-04-18 13:35:14 +00:00
const QDateTime login_timestamp ( QDateTime : : fromSecsSinceEpoch ( static_cast < qint64 > ( login_unix_timestamp ) ) ) ;
m_ui . loginStatus - > setText ( tr ( " Username: %1 \n Login token generated on %2. " )
. arg ( QString : : fromStdString ( username ) )
. arg ( login_timestamp . toString ( Qt : : TextDate ) ) ) ;
m_ui . loginButton - > setText ( tr ( " Logout " ) ) ;
}
else
{
m_ui . loginStatus - > setText ( tr ( " Not Logged In. " ) ) ;
m_ui . loginButton - > setText ( tr ( " Login... " ) ) ;
}
m_ui . viewProfile - > setEnabled ( logged_in ) ;
}
void AchievementSettingsWidget : : onLoginLogoutPressed ( )
{
if ( ! Host : : GetBaseStringSettingValue ( " Achievements " , " Username " ) . empty ( ) )
{
2023-09-17 10:19:51 +00:00
Host : : RunOnCPUThread ( [ ] ( ) { Achievements : : Logout ( ) ; } , true ) ;
2022-04-18 13:35:14 +00:00
updateLoginState ( ) ;
return ;
}
2023-07-27 09:24:55 +00:00
AchievementLoginDialog login ( this , Achievements : : LoginRequestReason : : UserInitiated ) ;
2022-04-18 13:35:14 +00:00
int res = login . exec ( ) ;
if ( res ! = 0 )
return ;
updateLoginState ( ) ;
2023-12-04 12:34:48 +00:00
// Login can enable achievements/hardcore.
if ( ! m_ui . enable - > isChecked ( ) & & Host : : GetBaseBoolSettingValue ( " Achievements " , " Enabled " , false ) )
{
QSignalBlocker sb ( m_ui . enable ) ;
m_ui . enable - > setChecked ( true ) ;
updateEnableState ( ) ;
}
if ( ! m_ui . hardcoreMode - > isChecked ( ) & & Host : : GetBaseBoolSettingValue ( " Achievements " , " ChallengeMode " , false ) )
{
QSignalBlocker sb ( m_ui . hardcoreMode ) ;
m_ui . hardcoreMode - > setChecked ( true ) ;
}
2022-04-18 13:35:14 +00:00
}
void AchievementSettingsWidget : : onViewProfilePressed ( )
{
const std : : string username ( Host : : GetBaseStringSettingValue ( " Achievements " , " Username " ) ) ;
if ( username . empty ( ) )
return ;
const QByteArray encoded_username ( QUrl : : toPercentEncoding ( QString : : fromStdString ( username ) ) ) ;
2023-09-17 10:19:51 +00:00
QtUtils : : OpenURL (
QtUtils : : GetRootWidget ( this ) ,
2022-04-18 13:35:14 +00:00
QUrl ( QStringLiteral ( " https://retroachievements.org/user/%1 " ) . arg ( QString : : fromUtf8 ( encoded_username ) ) ) ) ;
}
2023-09-17 10:19:51 +00:00
void AchievementSettingsWidget : : onAchievementsRefreshed ( quint32 id , const QString & game_info_string )
2022-04-18 13:35:14 +00:00
{
m_ui . gameInfo - > setText ( game_info_string ) ;
}