mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-04-26 18:05:43 -04:00
Achievements: Switch to rc_client
This commit is contained in:
parent
f8c5e4982c
commit
c773c763ef
@ -31,7 +31,6 @@ option(ENABLE_CUBEB "Build with Cubeb audio output" ON)
|
|||||||
option(ENABLE_OPENGL "Build with OpenGL renderer" ON)
|
option(ENABLE_OPENGL "Build with OpenGL renderer" ON)
|
||||||
option(ENABLE_VULKAN "Build with Vulkan renderer" ON)
|
option(ENABLE_VULKAN "Build with Vulkan renderer" ON)
|
||||||
option(ENABLE_DISCORD_PRESENCE "Build with Discord Rich Presence support" ON)
|
option(ENABLE_DISCORD_PRESENCE "Build with Discord Rich Presence support" ON)
|
||||||
option(ENABLE_CHEEVOS "Build with RetroAchievements support" ON)
|
|
||||||
#option(USE_SDL2 "Link with SDL2 for controller support" ON)
|
#option(USE_SDL2 "Link with SDL2 for controller support" ON)
|
||||||
set(USE_SDL2 ON)
|
set(USE_SDL2 ON)
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ add_subdirectory(googletest EXCLUDE_FROM_ALL)
|
|||||||
add_subdirectory(cpuinfo EXCLUDE_FROM_ALL)
|
add_subdirectory(cpuinfo EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(fast_float EXCLUDE_FROM_ALL)
|
add_subdirectory(fast_float EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(reshadefx EXCLUDE_FROM_ALL)
|
add_subdirectory(reshadefx EXCLUDE_FROM_ALL)
|
||||||
|
add_subdirectory(rcheevos EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
if(ENABLE_CUBEB)
|
if(ENABLE_CUBEB)
|
||||||
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
|
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
|
||||||
@ -29,10 +30,6 @@ if(ENABLE_DISCORD_PRESENCE)
|
|||||||
add_subdirectory(discord-rpc EXCLUDE_FROM_ALL)
|
add_subdirectory(discord-rpc EXCLUDE_FROM_ALL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ENABLE_CHEEVOS)
|
|
||||||
add_subdirectory(rcheevos EXCLUDE_FROM_ALL)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(${CPU_ARCH} STREQUAL "aarch32" OR ${CPU_ARCH} STREQUAL "aarch64")
|
if(${CPU_ARCH} STREQUAL "aarch32" OR ${CPU_ARCH} STREQUAL "aarch64")
|
||||||
add_subdirectory(vixl EXCLUDE_FROM_ALL)
|
add_subdirectory(vixl EXCLUDE_FROM_ALL)
|
||||||
endif()
|
endif()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
add_library(core
|
add_library(core
|
||||||
|
achievements.cpp
|
||||||
achievements.h
|
achievements.h
|
||||||
analog_controller.cpp
|
analog_controller.cpp
|
||||||
analog_controller.h
|
analog_controller.h
|
||||||
@ -123,7 +124,7 @@ target_precompile_headers(core PRIVATE "pch.h")
|
|||||||
target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||||
target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||||
target_link_libraries(core PUBLIC Threads::Threads common util zlib)
|
target_link_libraries(core PUBLIC Threads::Threads common util zlib)
|
||||||
target_link_libraries(core PRIVATE stb xxhash imgui rapidjson)
|
target_link_libraries(core PRIVATE stb xxhash imgui rapidjson rcheevos)
|
||||||
|
|
||||||
if(${CPU_ARCH} STREQUAL "x64")
|
if(${CPU_ARCH} STREQUAL "x64")
|
||||||
target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../../dep/xbyak/xbyak")
|
target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../../dep/xbyak/xbyak")
|
||||||
@ -154,14 +155,6 @@ else()
|
|||||||
message("Not building recompiler")
|
message("Not building recompiler")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ENABLE_CHEEVOS)
|
|
||||||
target_sources(core PRIVATE
|
|
||||||
achievements.cpp
|
|
||||||
)
|
|
||||||
target_compile_definitions(core PUBLIC -DWITH_CHEEVOS=1)
|
|
||||||
target_link_libraries(core PRIVATE rcheevos rapidjson)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ENABLE_DISCORD_PRESENCE)
|
if(ENABLE_DISCORD_PRESENCE)
|
||||||
target_compile_definitions(core PUBLIC -DWITH_DISCORD_PRESENCE=1)
|
target_compile_definitions(core PUBLIC -DWITH_DISCORD_PRESENCE=1)
|
||||||
target_link_libraries(core PRIVATE discord-rpc)
|
target_link_libraries(core PRIVATE discord-rpc)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -3,105 +3,44 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "settings.h"
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
#include "common/string.h"
|
#include "common/string.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class Error;
|
||||||
class StateWrapper;
|
class StateWrapper;
|
||||||
class CDImage;
|
class CDImage;
|
||||||
|
|
||||||
|
struct Settings;
|
||||||
|
|
||||||
namespace Achievements {
|
namespace Achievements {
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
enum class LoginRequestReason
|
||||||
|
|
||||||
enum class AchievementCategory : u8
|
|
||||||
{
|
{
|
||||||
Local = 0,
|
UserInitiated,
|
||||||
Core = 3,
|
TokenInvalid,
|
||||||
Unofficial = 5
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Achievement
|
|
||||||
{
|
|
||||||
u32 id;
|
|
||||||
std::string title;
|
|
||||||
std::string description;
|
|
||||||
std::string memaddr;
|
|
||||||
std::string badge_name;
|
|
||||||
|
|
||||||
// badge paths are mutable because they're resolved when they're needed.
|
|
||||||
mutable std::string locked_badge_path;
|
|
||||||
mutable std::string unlocked_badge_path;
|
|
||||||
|
|
||||||
u32 points;
|
|
||||||
AchievementCategory category;
|
|
||||||
bool locked;
|
|
||||||
bool active;
|
|
||||||
bool primed;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Leaderboard
|
|
||||||
{
|
|
||||||
u32 id;
|
|
||||||
std::string title;
|
|
||||||
std::string description;
|
|
||||||
int format;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LeaderboardEntry
|
|
||||||
{
|
|
||||||
std::string user;
|
|
||||||
std::string formatted_score;
|
|
||||||
time_t submitted;
|
|
||||||
u32 rank;
|
|
||||||
bool is_self;
|
|
||||||
};
|
|
||||||
|
|
||||||
// RAIntegration only exists for Windows, so no point checking it on other platforms.
|
|
||||||
#ifdef WITH_RAINTEGRATION
|
|
||||||
|
|
||||||
bool IsUsingRAIntegration();
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static ALWAYS_INLINE bool IsUsingRAIntegration()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool IsActive();
|
|
||||||
bool IsLoggedIn();
|
|
||||||
bool ChallengeModeActive();
|
|
||||||
bool LeaderboardsActive();
|
|
||||||
bool IsTestModeActive();
|
|
||||||
bool IsUnofficialTestModeActive();
|
|
||||||
bool IsRichPresenceEnabled();
|
|
||||||
bool HasActiveGame();
|
|
||||||
|
|
||||||
u32 GetGameID();
|
|
||||||
|
|
||||||
/// Acquires the achievements lock. Must be held when accessing any achievement state from another thread.
|
/// Acquires the achievements lock. Must be held when accessing any achievement state from another thread.
|
||||||
std::unique_lock<std::recursive_mutex> GetLock();
|
std::unique_lock<std::recursive_mutex> GetLock();
|
||||||
|
|
||||||
void Initialize();
|
/// Initializes the RetroAchievments client.
|
||||||
|
bool Initialize();
|
||||||
|
|
||||||
|
/// Updates achievements settings.
|
||||||
void UpdateSettings(const Settings& old_config);
|
void UpdateSettings(const Settings& old_config);
|
||||||
void ResetRuntime();
|
|
||||||
|
/// Resets the internal state of all achievement tracking. Call on system reset.
|
||||||
|
void ResetClient();
|
||||||
|
|
||||||
/// Called when the system is being reset. If it returns false, the reset should be aborted.
|
/// Called when the system is being reset. If it returns false, the reset should be aborted.
|
||||||
bool ConfirmSystemReset();
|
bool ConfirmSystemReset();
|
||||||
|
|
||||||
/// Called when the system is being shut down. If Shutdown() returns false, the shutdown should be aborted.
|
/// Called when the system is being shut down. If Shutdown() returns false, the shutdown should be aborted.
|
||||||
bool Shutdown();
|
bool Shutdown(bool allow_cancel);
|
||||||
|
|
||||||
/// Called when the system is being paused and resumed.
|
/// Called when the system is being paused and resumed.
|
||||||
void OnSystemPaused(bool paused);
|
void OnSystemPaused(bool paused);
|
||||||
@ -110,60 +49,86 @@ void OnSystemPaused(bool paused);
|
|||||||
void FrameUpdate();
|
void FrameUpdate();
|
||||||
|
|
||||||
/// Called when the system is paused, because FrameUpdate() won't be getting called.
|
/// Called when the system is paused, because FrameUpdate() won't be getting called.
|
||||||
void ProcessPendingHTTPRequests();
|
void IdleUpdate();
|
||||||
|
|
||||||
/// Saves/loads state.
|
/// Saves/loads state.
|
||||||
bool DoState(StateWrapper& sw);
|
bool DoState(StateWrapper& sw);
|
||||||
|
|
||||||
/// Returns true if the current game has any achievements or leaderboards.
|
/// Attempts to log in to RetroAchievements using the specified credentials.
|
||||||
/// Does not need to have the lock held.
|
/// If the login is successful, the token returned by the server will be saved.
|
||||||
bool SafeHasAchievementsOrLeaderboards();
|
bool Login(const char* username, const char* password, Error* error);
|
||||||
|
|
||||||
const std::string& GetUsername();
|
/// Logs out of RetroAchievements, clearing any credentials.
|
||||||
const std::string& GetRichPresenceString();
|
|
||||||
|
|
||||||
bool LoginAsync(const char* username, const char* password);
|
|
||||||
bool Login(const char* username, const char* password);
|
|
||||||
void Logout();
|
void Logout();
|
||||||
|
|
||||||
|
/// Called when the system changes game, or is booting.
|
||||||
void GameChanged(const std::string& path, CDImage* image);
|
void GameChanged(const std::string& path, CDImage* image);
|
||||||
|
|
||||||
/// Re-enables hardcode mode if it is enabled in the settings.
|
/// Re-enables hardcode mode if it is enabled in the settings.
|
||||||
bool ResetChallengeMode();
|
bool ResetHardcoreMode();
|
||||||
|
|
||||||
/// Forces hardcore mode off until next reset.
|
/// Forces hardcore mode off until next reset.
|
||||||
void DisableChallengeMode();
|
void DisableHardcoreMode();
|
||||||
|
|
||||||
/// Prompts the user to disable hardcore mode, if they agree, returns true.
|
/// Prompts the user to disable hardcore mode, if they agree, returns true.
|
||||||
bool ConfirmChallengeModeDisable(const char* trigger);
|
bool ConfirmHardcoreModeDisable(const char* trigger);
|
||||||
|
|
||||||
/// Returns true if features such as save states should be disabled.
|
/// Returns true if hardcore mode is active, and functionality should be restricted.
|
||||||
bool ChallengeModeActive();
|
bool IsHardcoreModeActive();
|
||||||
|
|
||||||
|
/// RAIntegration only exists for Windows, so no point checking it on other platforms.
|
||||||
|
bool IsUsingRAIntegration();
|
||||||
|
|
||||||
|
/// Returns true if the achievement system is active. Achievements can be active without a valid client.
|
||||||
|
bool IsActive();
|
||||||
|
|
||||||
|
/// Returns true if RetroAchievements game data has been loaded.
|
||||||
|
bool HasActiveGame();
|
||||||
|
|
||||||
|
/// Returns the RetroAchievements ID for the current game.
|
||||||
|
u32 GetGameID();
|
||||||
|
|
||||||
|
/// Returns true if the current game has any achievements or leaderboards.
|
||||||
|
bool HasAchievementsOrLeaderboards();
|
||||||
|
|
||||||
|
/// Returns true if the current game has any leaderboards.
|
||||||
|
bool HasLeaderboards();
|
||||||
|
|
||||||
|
/// Returns true if the game supports rich presence.
|
||||||
|
bool HasRichPresence();
|
||||||
|
|
||||||
|
/// Returns the current rich presence string.
|
||||||
|
/// Should be called with the lock held.
|
||||||
|
const std::string& GetRichPresenceString();
|
||||||
|
|
||||||
|
/// Returns the RetroAchievements title for the current game.
|
||||||
|
/// Should be called with the lock held.
|
||||||
const std::string& GetGameTitle();
|
const std::string& GetGameTitle();
|
||||||
const std::string& GetGameIcon();
|
|
||||||
|
|
||||||
bool EnumerateAchievements(std::function<bool(const Achievement&)> callback);
|
/// Clears all cached state used to render the UI.
|
||||||
u32 GetUnlockedAchiementCount();
|
void ClearUIState();
|
||||||
u32 GetAchievementCount();
|
|
||||||
u32 GetMaximumPointsForGame();
|
|
||||||
u32 GetCurrentPointsForGame();
|
|
||||||
|
|
||||||
bool EnumerateLeaderboards(std::function<bool(const Leaderboard&)> callback);
|
/// Draws ImGui overlays when not paused.
|
||||||
std::optional<bool> TryEnumerateLeaderboardEntries(u32 id, std::function<bool(const LeaderboardEntry&)> callback);
|
void DrawGameOverlays();
|
||||||
const Leaderboard* GetLeaderboardByID(u32 id);
|
|
||||||
u32 GetLeaderboardCount();
|
|
||||||
bool IsLeaderboardTimeType(const Leaderboard& leaderboard);
|
|
||||||
u32 GetPrimedAchievementCount();
|
|
||||||
|
|
||||||
const Achievement* GetAchievementByID(u32 id);
|
/// Draws ImGui overlays when paused.
|
||||||
std::pair<u32, u32> GetAchievementProgress(const Achievement& achievement);
|
void DrawPauseMenuOverlays();
|
||||||
TinyString GetAchievementProgressText(const Achievement& achievement);
|
|
||||||
const std::string& GetAchievementBadgePath(const Achievement& achievement, bool download_if_missing = true,
|
/// Queries the achievement list, and if no achievements are available, returns false.
|
||||||
bool force_unlocked_icon = false);
|
bool PrepareAchievementsWindow();
|
||||||
std::string GetAchievementBadgeURL(const Achievement& achievement);
|
|
||||||
|
/// Renders the achievement list.
|
||||||
|
void DrawAchievementsWindow();
|
||||||
|
|
||||||
|
/// Queries the leaderboard list, and if no leaderboards are available, returns false.
|
||||||
|
bool PrepareLeaderboardsWindow();
|
||||||
|
|
||||||
|
/// Renders the leaderboard list.
|
||||||
|
void DrawLeaderboardsWindow();
|
||||||
|
|
||||||
#ifdef WITH_RAINTEGRATION
|
#ifdef WITH_RAINTEGRATION
|
||||||
|
/// Prevents the internal implementation from being used. Instead, RAIntegration will be
|
||||||
|
/// called into when achievement-related events occur.
|
||||||
void SwitchToRAIntegration();
|
void SwitchToRAIntegration();
|
||||||
|
|
||||||
namespace RAIntegration {
|
namespace RAIntegration {
|
||||||
@ -173,46 +138,20 @@ std::vector<std::tuple<int, std::string, bool>> GetMenuItems();
|
|||||||
void ActivateMenuItem(int item);
|
void ActivateMenuItem(int item);
|
||||||
} // namespace RAIntegration
|
} // namespace RAIntegration
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// Make noops when compiling without cheevos.
|
|
||||||
static inline bool ConfirmSystemReset()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
static inline void ResetRuntime()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
static inline bool DoState(StateWrapper& sw)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
static constexpr inline bool ChallengeModeActive()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool ResetChallengeMode()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void DisableChallengeMode()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool ConfirmChallengeModeDisable(const char* trigger)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace Achievements
|
} // namespace Achievements
|
||||||
|
|
||||||
/// Functions implemented in the frontend.
|
/// Functions implemented in the frontend.
|
||||||
namespace Host {
|
namespace Host {
|
||||||
|
/// Called if the big picture UI requests achievements login, or token login fails.
|
||||||
|
void OnAchievementsLoginRequested(Achievements::LoginRequestReason reason);
|
||||||
|
|
||||||
|
/// Called when achievements login completes.
|
||||||
|
void OnAchievementsLoginSuccess(const char* display_name, u32 points, u32 sc_points, u32 unread_messages);
|
||||||
|
|
||||||
|
/// Called whenever game details or rich presence information is updated.
|
||||||
|
/// Implementers can assume the lock is held when this is called.
|
||||||
void OnAchievementsRefreshed();
|
void OnAchievementsRefreshed();
|
||||||
void OnAchievementsChallengeModeChanged();
|
|
||||||
|
/// Called whenever hardcore mode is toggled.
|
||||||
|
void OnAchievementsHardcoreModeChanged();
|
||||||
} // namespace Host
|
} // namespace Host
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<PreprocessorDefinitions>WITH_CHEEVOS=1;WITH_DISCORD_PRESENCE=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions Condition="('$(Platform)'!='ARM64')">WITH_RAINTEGRATION=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions Condition="('$(Platform)'!='ARM64')">WITH_RAINTEGRATION=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM' Or '$(Platform)'=='ARM64')">WITH_RECOMPILER=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM' Or '$(Platform)'=='ARM64')">WITH_RECOMPILER=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM64')">WITH_MMAP_FASTMEM=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM64')">WITH_MMAP_FASTMEM=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class GPUTexture;
|
class String;
|
||||||
|
|
||||||
struct Settings;
|
struct Settings;
|
||||||
|
|
||||||
@ -23,11 +23,15 @@ void OnSystemDestroyed();
|
|||||||
void OnRunningGameChanged();
|
void OnRunningGameChanged();
|
||||||
void OpenPauseMenu();
|
void OpenPauseMenu();
|
||||||
bool OpenAchievementsWindow();
|
bool OpenAchievementsWindow();
|
||||||
|
bool IsAchievementsWindowOpen();
|
||||||
bool OpenLeaderboardsWindow();
|
bool OpenLeaderboardsWindow();
|
||||||
|
bool IsLeaderboardsWindowOpen();
|
||||||
|
void ReturnToMainWindow();
|
||||||
|
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
void Render();
|
void Render();
|
||||||
void InvalidateCoverCache();
|
void InvalidateCoverCache();
|
||||||
|
void TimeToPrintableString(String* str, time_t t);
|
||||||
|
|
||||||
// Returns true if the message has been dismissed.
|
// Returns true if the message has been dismissed.
|
||||||
bool DrawErrorWindow(const char* message);
|
bool DrawErrorWindow(const char* message);
|
||||||
|
@ -160,7 +160,7 @@ DEFINE_HOTKEY("Screenshot", TRANSLATE_NOOP("Hotkeys", "General"), TRANSLATE_NOOP
|
|||||||
System::SaveScreenshot();
|
System::SaveScreenshot();
|
||||||
})
|
})
|
||||||
|
|
||||||
#if !defined(__ANDROID__) && defined(WITH_CHEEVOS)
|
#if !defined(__ANDROID__)
|
||||||
DEFINE_HOTKEY("OpenAchievements", TRANSLATE_NOOP("Hotkeys", "General"),
|
DEFINE_HOTKEY("OpenAchievements", TRANSLATE_NOOP("Hotkeys", "General"),
|
||||||
TRANSLATE_NOOP("Hotkeys", "Open Achievement List"), [](s32 pressed) {
|
TRANSLATE_NOOP("Hotkeys", "Open Achievement List"), [](s32 pressed) {
|
||||||
if (!pressed)
|
if (!pressed)
|
||||||
@ -184,7 +184,7 @@ DEFINE_HOTKEY("OpenLeaderboards", TRANSLATE_NOOP("Hotkeys", "General"),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
#endif // !defined(__ANDROID__) && defined(WITH_CHEEVOS)
|
#endif // !defined(__ANDROID__)
|
||||||
|
|
||||||
DEFINE_HOTKEY("Reset", TRANSLATE_NOOP("Hotkeys", "System"), TRANSLATE_NOOP("Hotkeys", "Reset System"), [](s32 pressed) {
|
DEFINE_HOTKEY("Reset", TRANSLATE_NOOP("Hotkeys", "System"), TRANSLATE_NOOP("Hotkeys", "Reset System"), [](s32 pressed) {
|
||||||
if (!pressed)
|
if (!pressed)
|
||||||
|
@ -365,16 +365,20 @@ void Settings::Load(SettingsInterface& si)
|
|||||||
memory_card_use_playlist_title = si.GetBoolValue("MemoryCards", "UsePlaylistTitle", true);
|
memory_card_use_playlist_title = si.GetBoolValue("MemoryCards", "UsePlaylistTitle", true);
|
||||||
|
|
||||||
achievements_enabled = si.GetBoolValue("Cheevos", "Enabled", false);
|
achievements_enabled = si.GetBoolValue("Cheevos", "Enabled", false);
|
||||||
achievements_test_mode = si.GetBoolValue("Cheevos", "TestMode", 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_unofficial_test_mode = si.GetBoolValue("Cheevos", "UnofficialTestMode", false);
|
||||||
achievements_use_first_disc_from_playlist = si.GetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", true);
|
achievements_use_first_disc_from_playlist = si.GetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", true);
|
||||||
achievements_rich_presence = si.GetBoolValue("Cheevos", "RichPresence", true);
|
|
||||||
achievements_challenge_mode = si.GetBoolValue("Cheevos", "ChallengeMode", false);
|
|
||||||
achievements_leaderboards = si.GetBoolValue("Cheevos", "Leaderboards", true);
|
|
||||||
achievements_notifications = si.GetBoolValue("Cheevos", "Notifications", true);
|
|
||||||
achievements_sound_effects = si.GetBoolValue("Cheevos", "SoundEffects", true);
|
|
||||||
achievements_primed_indicators = si.GetBoolValue("Cheevos", "PrimedIndicators", true);
|
|
||||||
achievements_use_raintegration = si.GetBoolValue("Cheevos", "UseRAIntegration", false);
|
achievements_use_raintegration = si.GetBoolValue("Cheevos", "UseRAIntegration", false);
|
||||||
|
achievements_notification_duration =
|
||||||
|
si.GetFloatValue("Cheevos", "NotificationsDuration", DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME);
|
||||||
|
achievements_leaderboard_duration =
|
||||||
|
si.GetFloatValue("Cheevos", "LeaderboardsDuration", DEFAULT_LEADERBOARD_NOTIFICATION_TIME);
|
||||||
|
|
||||||
log_level = ParseLogLevelName(si.GetStringValue("Logging", "LogLevel", GetLogLevelName(DEFAULT_LOG_LEVEL)).c_str())
|
log_level = ParseLogLevelName(si.GetStringValue("Logging", "LogLevel", GetLogLevelName(DEFAULT_LOG_LEVEL)).c_str())
|
||||||
.value_or(DEFAULT_LOG_LEVEL);
|
.value_or(DEFAULT_LOG_LEVEL);
|
||||||
@ -564,16 +568,18 @@ void Settings::Save(SettingsInterface& si) const
|
|||||||
si.SetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(multitap_mode));
|
si.SetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(multitap_mode));
|
||||||
|
|
||||||
si.SetBoolValue("Cheevos", "Enabled", achievements_enabled);
|
si.SetBoolValue("Cheevos", "Enabled", achievements_enabled);
|
||||||
si.SetBoolValue("Cheevos", "TestMode", achievements_test_mode);
|
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", "UnofficialTestMode", achievements_unofficial_test_mode);
|
||||||
si.SetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", achievements_use_first_disc_from_playlist);
|
si.SetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", achievements_use_first_disc_from_playlist);
|
||||||
si.SetBoolValue("Cheevos", "RichPresence", achievements_rich_presence);
|
|
||||||
si.SetBoolValue("Cheevos", "ChallengeMode", achievements_challenge_mode);
|
|
||||||
si.SetBoolValue("Cheevos", "Leaderboards", achievements_leaderboards);
|
|
||||||
si.SetBoolValue("Cheevos", "Notifications", achievements_notifications);
|
|
||||||
si.SetBoolValue("Cheevos", "SoundEffects", achievements_sound_effects);
|
|
||||||
si.SetBoolValue("Cheevos", "PrimedIndicators", achievements_primed_indicators);
|
|
||||||
si.SetBoolValue("Cheevos", "UseRAIntegration", achievements_use_raintegration);
|
si.SetBoolValue("Cheevos", "UseRAIntegration", achievements_use_raintegration);
|
||||||
|
si.SetFloatValue("Cheevos", "NotificationsDuration", achievements_notification_duration);
|
||||||
|
si.SetFloatValue("Cheevos", "LeaderboardsDuration", achievements_leaderboard_duration);
|
||||||
|
|
||||||
si.SetStringValue("Logging", "LogLevel", GetLogLevelName(log_level));
|
si.SetStringValue("Logging", "LogLevel", GetLogLevelName(log_level));
|
||||||
si.SetStringValue("Logging", "LogFilter", log_filter.c_str());
|
si.SetStringValue("Logging", "LogFilter", log_filter.c_str());
|
||||||
@ -696,7 +702,7 @@ void Settings::FixIncompatibleSettings(bool display_osd_messages)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if challenge mode is enabled, disable things like rewind since they use save states
|
// if challenge mode is enabled, disable things like rewind since they use save states
|
||||||
if (Achievements::ChallengeModeActive())
|
if (Achievements::IsHardcoreModeActive())
|
||||||
{
|
{
|
||||||
g_settings.emulation_speed =
|
g_settings.emulation_speed =
|
||||||
(g_settings.emulation_speed != 0.0f) ? std::max(g_settings.emulation_speed, 1.0f) : 0.0f;
|
(g_settings.emulation_speed != 0.0f) ? std::max(g_settings.emulation_speed, 1.0f) : 0.0f;
|
||||||
|
@ -180,16 +180,18 @@ struct Settings
|
|||||||
|
|
||||||
// achievements
|
// achievements
|
||||||
bool achievements_enabled = false;
|
bool achievements_enabled = false;
|
||||||
bool achievements_test_mode = false;
|
bool achievements_hardcore_mode = false;
|
||||||
|
bool achievements_notifications = true;
|
||||||
|
bool achievements_leaderboard_notifications = true;
|
||||||
|
bool achievements_sound_effects = true;
|
||||||
|
bool achievements_overlays = true;
|
||||||
|
bool achievements_encore_mode = false;
|
||||||
|
bool achievements_spectator_mode = false;
|
||||||
bool achievements_unofficial_test_mode = false;
|
bool achievements_unofficial_test_mode = false;
|
||||||
bool achievements_use_first_disc_from_playlist = true;
|
bool achievements_use_first_disc_from_playlist = true;
|
||||||
bool achievements_rich_presence = true;
|
|
||||||
bool achievements_challenge_mode = false;
|
|
||||||
bool achievements_leaderboards = true;
|
|
||||||
bool achievements_notifications = true;
|
|
||||||
bool achievements_sound_effects = true;
|
|
||||||
bool achievements_primed_indicators = true;
|
|
||||||
bool achievements_use_raintegration = false;
|
bool achievements_use_raintegration = false;
|
||||||
|
float achievements_notification_duration = DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME;
|
||||||
|
float achievements_leaderboard_duration = DEFAULT_LEADERBOARD_NOTIFICATION_TIME;
|
||||||
|
|
||||||
struct DebugSettings
|
struct DebugSettings
|
||||||
{
|
{
|
||||||
@ -473,6 +475,9 @@ struct Settings
|
|||||||
static constexpr MemoryCardType DEFAULT_MEMORY_CARD_2_TYPE = MemoryCardType::None;
|
static constexpr MemoryCardType DEFAULT_MEMORY_CARD_2_TYPE = MemoryCardType::None;
|
||||||
static constexpr MultitapMode DEFAULT_MULTITAP_MODE = MultitapMode::Disabled;
|
static constexpr MultitapMode DEFAULT_MULTITAP_MODE = MultitapMode::Disabled;
|
||||||
|
|
||||||
|
static constexpr float DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME = 10.0f;
|
||||||
|
static constexpr float DEFAULT_LEADERBOARD_NOTIFICATION_TIME = 10.0f;
|
||||||
|
|
||||||
static constexpr LOGLEVEL DEFAULT_LOG_LEVEL = LOGLEVEL_INFO;
|
static constexpr LOGLEVEL DEFAULT_LOG_LEVEL = LOGLEVEL_INFO;
|
||||||
|
|
||||||
#ifndef __ANDROID__
|
#ifndef __ANDROID__
|
||||||
|
@ -135,7 +135,6 @@ static void SetTimerResolutionIncreased(bool enabled);
|
|||||||
#ifdef WITH_DISCORD_PRESENCE
|
#ifdef WITH_DISCORD_PRESENCE
|
||||||
static void InitializeDiscordPresence();
|
static void InitializeDiscordPresence();
|
||||||
static void ShutdownDiscordPresence();
|
static void ShutdownDiscordPresence();
|
||||||
static void UpdateDiscordPresence(bool rich_presence_only);
|
|
||||||
static void PollDiscordPresence();
|
static void PollDiscordPresence();
|
||||||
#endif
|
#endif
|
||||||
} // namespace System
|
} // namespace System
|
||||||
@ -233,11 +232,7 @@ static u32 s_runahead_replay_frames = 0;
|
|||||||
static u64 s_session_start_time = 0;
|
static u64 s_session_start_time = 0;
|
||||||
|
|
||||||
#ifdef WITH_DISCORD_PRESENCE
|
#ifdef WITH_DISCORD_PRESENCE
|
||||||
// discord rich presence
|
|
||||||
static bool s_discord_presence_active = false;
|
static bool s_discord_presence_active = false;
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
static std::string s_discord_presence_cheevos_string;
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static TinyString GetTimestampStringForFileName()
|
static TinyString GetTimestampStringForFileName()
|
||||||
@ -250,14 +245,12 @@ void System::Internal::ProcessStartup()
|
|||||||
// This will call back to Host::LoadSettings() -> ReloadSources().
|
// This will call back to Host::LoadSettings() -> ReloadSources().
|
||||||
LoadSettings(false);
|
LoadSettings(false);
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
#ifdef WITH_RAINTEGRATION
|
#ifdef WITH_RAINTEGRATION
|
||||||
if (Host::GetBaseBoolSettingValue("Cheevos", "UseRAIntegration", false))
|
if (Host::GetBaseBoolSettingValue("Cheevos", "UseRAIntegration", false))
|
||||||
Achievements::SwitchToRAIntegration();
|
Achievements::SwitchToRAIntegration();
|
||||||
#endif
|
#endif
|
||||||
if (g_settings.achievements_enabled)
|
if (g_settings.achievements_enabled)
|
||||||
Achievements::Initialize();
|
Achievements::Initialize();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::Internal::ProcessShutdown()
|
void System::Internal::ProcessShutdown()
|
||||||
@ -266,9 +259,7 @@ void System::Internal::ProcessShutdown()
|
|||||||
ShutdownDiscordPresence();
|
ShutdownDiscordPresence();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
Achievements::Shutdown(false);
|
||||||
Achievements::Shutdown();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
InputManager::CloseSources();
|
InputManager::CloseSources();
|
||||||
}
|
}
|
||||||
@ -281,9 +272,7 @@ void System::Internal::IdlePollUpdate()
|
|||||||
PollDiscordPresence();
|
PollDiscordPresence();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
Achievements::IdleUpdate();
|
||||||
Achievements::ProcessPendingHTTPRequests();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
System::State System::GetState()
|
System::State System::GetState()
|
||||||
@ -1068,13 +1057,11 @@ void System::ResetSystem()
|
|||||||
if (!IsValid())
|
if (!IsValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
if (!Achievements::ConfirmSystemReset())
|
if (!Achievements::ConfirmSystemReset())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Achievements::ResetChallengeMode())
|
if (Achievements::ResetHardcoreMode())
|
||||||
ApplySettings(false);
|
ApplySettings(false);
|
||||||
#endif
|
|
||||||
|
|
||||||
InternalReset();
|
InternalReset();
|
||||||
ResetPerformanceCounters();
|
ResetPerformanceCounters();
|
||||||
@ -1096,9 +1083,7 @@ void System::PauseSystem(bool paused)
|
|||||||
|
|
||||||
InputManager::PauseVibration();
|
InputManager::PauseVibration();
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
Achievements::OnSystemPaused(true);
|
Achievements::OnSystemPaused(true);
|
||||||
#endif
|
|
||||||
|
|
||||||
if (g_settings.inhibit_screensaver)
|
if (g_settings.inhibit_screensaver)
|
||||||
PlatformMisc::ResumeScreensaver();
|
PlatformMisc::ResumeScreensaver();
|
||||||
@ -1110,9 +1095,7 @@ void System::PauseSystem(bool paused)
|
|||||||
{
|
{
|
||||||
FullscreenUI::OnSystemResumed();
|
FullscreenUI::OnSystemResumed();
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
Achievements::OnSystemPaused(false);
|
Achievements::OnSystemPaused(false);
|
||||||
#endif
|
|
||||||
|
|
||||||
if (g_settings.inhibit_screensaver)
|
if (g_settings.inhibit_screensaver)
|
||||||
PlatformMisc::SuspendScreensaver();
|
PlatformMisc::SuspendScreensaver();
|
||||||
@ -1131,13 +1114,11 @@ bool System::LoadState(const char* filename)
|
|||||||
if (!IsValid())
|
if (!IsValid())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
if (Achievements::IsHardcoreModeActive() &&
|
||||||
if (Achievements::ChallengeModeActive() &&
|
!Achievements::ConfirmHardcoreModeDisable(TRANSLATE("Achievements", "Loading state")))
|
||||||
!Achievements::ConfirmChallengeModeDisable(TRANSLATE("Achievements", "Loading state")))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
Common::Timer load_timer;
|
Common::Timer load_timer;
|
||||||
|
|
||||||
@ -1265,11 +1246,11 @@ bool System::BootSystem(SystemBootParameters parameters)
|
|||||||
(do_exe_boot ? GetRegionForExe(parameters.filename.c_str()) : GetRegionForPsf(parameters.filename.c_str()));
|
(do_exe_boot ? GetRegionForExe(parameters.filename.c_str()) : GetRegionForPsf(parameters.filename.c_str()));
|
||||||
Log_InfoPrintf("EXE/PSF Region: %s", Settings::GetDiscRegionDisplayName(file_region));
|
Log_InfoPrintf("EXE/PSF Region: %s", Settings::GetDiscRegionDisplayName(file_region));
|
||||||
s_region = GetConsoleRegionForDiscRegion(file_region);
|
s_region = GetConsoleRegionForDiscRegion(file_region);
|
||||||
if (do_psf_boot)
|
|
||||||
psf_boot = std::move(parameters.filename);
|
|
||||||
else
|
|
||||||
exe_boot = std::move(parameters.filename);
|
|
||||||
}
|
}
|
||||||
|
if (do_psf_boot)
|
||||||
|
psf_boot = std::move(parameters.filename);
|
||||||
|
else
|
||||||
|
exe_boot = std::move(parameters.filename);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1350,17 +1331,15 @@ bool System::BootSystem(SystemBootParameters parameters)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
// Check for resuming with hardcore mode.
|
// Check for resuming with hardcore mode.
|
||||||
if (!parameters.save_state.empty() && Achievements::ChallengeModeActive() &&
|
if (!parameters.save_state.empty() && Achievements::IsHardcoreModeActive() &&
|
||||||
!Achievements::ConfirmChallengeModeDisable(TRANSLATE("Achievements", "Resuming state")))
|
!Achievements::ConfirmHardcoreModeDisable(TRANSLATE("Achievements", "Resuming state")))
|
||||||
{
|
{
|
||||||
s_state = State::Shutdown;
|
s_state = State::Shutdown;
|
||||||
ClearRunningGame();
|
ClearRunningGame();
|
||||||
Host::OnSystemDestroyed();
|
Host::OnSystemDestroyed();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Load BIOS image.
|
// Load BIOS image.
|
||||||
if (!LoadBIOS(parameters.override_bios))
|
if (!LoadBIOS(parameters.override_bios))
|
||||||
@ -1683,12 +1662,10 @@ void System::ClearRunningGame()
|
|||||||
|
|
||||||
Host::OnGameChanged(s_running_game_path, s_running_game_serial, s_running_game_title);
|
Host::OnGameChanged(s_running_game_path, s_running_game_serial, s_running_game_title);
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
Achievements::GameChanged(s_running_game_path, nullptr);
|
Achievements::GameChanged(s_running_game_path, nullptr);
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef WITH_DISCORD_PRESENCE
|
#ifdef WITH_DISCORD_PRESENCE
|
||||||
UpdateDiscordPresence(false);
|
UpdateDiscordPresence();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1760,10 +1737,8 @@ void System::FrameDone()
|
|||||||
if (s_cheat_list)
|
if (s_cheat_list)
|
||||||
s_cheat_list->Apply();
|
s_cheat_list->Apply();
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
if (Achievements::IsActive())
|
if (Achievements::IsActive())
|
||||||
Achievements::FrameUpdate();
|
Achievements::FrameUpdate();
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef WITH_DISCORD_PRESENCE
|
#ifdef WITH_DISCORD_PRESENCE
|
||||||
PollDiscordPresence();
|
PollDiscordPresence();
|
||||||
@ -2131,23 +2106,13 @@ bool System::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_di
|
|||||||
if (!sw.DoMarker("Cheevos"))
|
if (!sw.DoMarker("Cheevos"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
if (!Achievements::DoState(sw))
|
if (!Achievements::DoState(sw))
|
||||||
return false;
|
return false;
|
||||||
#else
|
|
||||||
// if we compiled without cheevos, we need to toss out the data from states which were
|
|
||||||
u32 data_size = 0;
|
|
||||||
sw.Do(&data_size);
|
|
||||||
if (data_size > 0)
|
|
||||||
sw.SkipBytes(data_size);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
// loading an old state without cheevos, so reset the runtime
|
// loading an old state without cheevos, so reset the runtime
|
||||||
Achievements::ResetRuntime();
|
Achievements::ResetClient();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2208,9 +2173,7 @@ void System::InternalReset()
|
|||||||
TimingEvents::Reset();
|
TimingEvents::Reset();
|
||||||
ResetPerformanceCounters();
|
ResetPerformanceCounters();
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
Achievements::ResetClient();
|
||||||
Achievements::ResetRuntime();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string System::GetMediaPathFromSaveState(const char* path)
|
std::string System::GetMediaPathFromSaveState(const char* path)
|
||||||
@ -2352,15 +2315,13 @@ bool System::LoadStateFromStream(ByteStream* state, bool update_display, bool ig
|
|||||||
|
|
||||||
ClearMemorySaveStates();
|
ClearMemorySaveStates();
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
// Updating game/loading settings can turn on hardcore mode. Catch this.
|
// Updating game/loading settings can turn on hardcore mode. Catch this.
|
||||||
if (Achievements::ChallengeModeActive())
|
if (Achievements::IsHardcoreModeActive())
|
||||||
{
|
{
|
||||||
Host::AddKeyedOSDMessage("challenge_mode_reset",
|
Host::AddKeyedOSDMessage("challenge_mode_reset",
|
||||||
TRANSLATE_STR("Achievements", "Hardcore mode disabled by state switch."), 10.0f);
|
TRANSLATE_STR("Achievements", "Hardcore mode disabled by state switch."), 10.0f);
|
||||||
Achievements::DisableChallengeMode();
|
Achievements::DisableHardcoreMode();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!state->SeekAbsolute(header.offset_to_data))
|
if (!state->SeekAbsolute(header.offset_to_data))
|
||||||
return false;
|
return false;
|
||||||
@ -2720,10 +2681,8 @@ void System::SetRewindState(bool enabled)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
if (Achievements::IsHardcoreModeActive() && !Achievements::ConfirmHardcoreModeDisable("Rewinding"))
|
||||||
if (Achievements::ChallengeModeActive() && !Achievements::ConfirmChallengeModeDisable("Rewinding"))
|
|
||||||
return;
|
return;
|
||||||
#endif
|
|
||||||
|
|
||||||
System::SetRewinding(enabled);
|
System::SetRewinding(enabled);
|
||||||
UpdateSpeedLimiterState();
|
UpdateSpeedLimiterState();
|
||||||
@ -2734,10 +2693,8 @@ void System::DoFrameStep()
|
|||||||
if (!IsValid())
|
if (!IsValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
if (Achievements::IsHardcoreModeActive() && !Achievements::ConfirmHardcoreModeDisable("Frame stepping"))
|
||||||
if (Achievements::ChallengeModeActive() && !Achievements::ConfirmChallengeModeDisable("Frame stepping"))
|
|
||||||
return;
|
return;
|
||||||
#endif
|
|
||||||
|
|
||||||
s_frame_step_request = true;
|
s_frame_step_request = true;
|
||||||
PauseSystem(false);
|
PauseSystem(false);
|
||||||
@ -2748,10 +2705,8 @@ void System::DoToggleCheats()
|
|||||||
if (!System::IsValid())
|
if (!System::IsValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
if (Achievements::IsHardcoreModeActive() && !Achievements::ConfirmHardcoreModeDisable("Toggling cheats"))
|
||||||
if (Achievements::ChallengeModeActive() && !Achievements::ConfirmChallengeModeDisable("Toggling cheats"))
|
|
||||||
return;
|
return;
|
||||||
#endif
|
|
||||||
|
|
||||||
CheatList* cl = GetCheatList();
|
CheatList* cl = GetCheatList();
|
||||||
if (!cl)
|
if (!cl)
|
||||||
@ -3341,18 +3296,16 @@ void System::UpdateRunningGame(const char* path, CDImage* image, bool booting)
|
|||||||
|
|
||||||
g_texture_replacements.SetGameID(s_running_game_serial);
|
g_texture_replacements.SetGameID(s_running_game_serial);
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
if (booting)
|
if (booting)
|
||||||
Achievements::ResetChallengeMode();
|
Achievements::ResetHardcoreMode();
|
||||||
|
|
||||||
Achievements::GameChanged(s_running_game_path, image);
|
Achievements::GameChanged(s_running_game_path, image);
|
||||||
#endif
|
|
||||||
|
|
||||||
UpdateGameSettingsLayer();
|
UpdateGameSettingsLayer();
|
||||||
ApplySettings(true);
|
ApplySettings(true);
|
||||||
|
|
||||||
s_cheat_list.reset();
|
s_cheat_list.reset();
|
||||||
if (g_settings.auto_load_cheats && !Achievements::ChallengeModeActive())
|
if (g_settings.auto_load_cheats && !Achievements::IsHardcoreModeActive())
|
||||||
LoadCheatListFromGameTitle();
|
LoadCheatListFromGameTitle();
|
||||||
|
|
||||||
if (s_running_game_serial != prev_serial)
|
if (s_running_game_serial != prev_serial)
|
||||||
@ -3361,7 +3314,7 @@ void System::UpdateRunningGame(const char* path, CDImage* image, bool booting)
|
|||||||
SaveStateSelectorUI::RefreshList();
|
SaveStateSelectorUI::RefreshList();
|
||||||
|
|
||||||
#ifdef WITH_DISCORD_PRESENCE
|
#ifdef WITH_DISCORD_PRESENCE
|
||||||
UpdateDiscordPresence(false);
|
UpdateDiscordPresence();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Host::OnGameChanged(s_running_game_path, s_running_game_serial, s_running_game_title);
|
Host::OnGameChanged(s_running_game_path, s_running_game_serial, s_running_game_title);
|
||||||
@ -3732,9 +3685,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
|||||||
if (g_settings.multitap_mode != old_settings.multitap_mode)
|
if (g_settings.multitap_mode != old_settings.multitap_mode)
|
||||||
UpdateMultitaps();
|
UpdateMultitaps();
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
Achievements::UpdateSettings(old_settings);
|
Achievements::UpdateSettings(old_settings);
|
||||||
#endif
|
|
||||||
|
|
||||||
FullscreenUI::CheckForConfigChanges(old_settings);
|
FullscreenUI::CheckForConfigChanges(old_settings);
|
||||||
|
|
||||||
@ -4417,7 +4368,7 @@ bool System::LoadCheatList(const char* filename)
|
|||||||
bool System::LoadCheatListFromGameTitle()
|
bool System::LoadCheatListFromGameTitle()
|
||||||
{
|
{
|
||||||
// Called when booting, needs to test for shutdown.
|
// Called when booting, needs to test for shutdown.
|
||||||
if (IsShutdown() || Achievements::ChallengeModeActive())
|
if (IsShutdown() || Achievements::IsHardcoreModeActive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const std::string filename(GetCheatFileName());
|
const std::string filename(GetCheatFileName());
|
||||||
@ -4429,7 +4380,7 @@ bool System::LoadCheatListFromGameTitle()
|
|||||||
|
|
||||||
bool System::LoadCheatListFromDatabase()
|
bool System::LoadCheatListFromDatabase()
|
||||||
{
|
{
|
||||||
if (IsShutdown() || s_running_game_serial.empty() || Achievements::ChallengeModeActive())
|
if (IsShutdown() || s_running_game_serial.empty() || Achievements::IsHardcoreModeActive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::unique_ptr<CheatList> cl = std::make_unique<CheatList>();
|
std::unique_ptr<CheatList> cl = std::make_unique<CheatList>();
|
||||||
@ -4775,7 +4726,7 @@ void System::InitializeDiscordPresence()
|
|||||||
Discord_Initialize("705325712680288296", &handlers, 0, nullptr);
|
Discord_Initialize("705325712680288296", &handlers, 0, nullptr);
|
||||||
s_discord_presence_active = true;
|
s_discord_presence_active = true;
|
||||||
|
|
||||||
UpdateDiscordPresence(false);
|
UpdateDiscordPresence();
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::ShutdownDiscordPresence()
|
void System::ShutdownDiscordPresence()
|
||||||
@ -4786,31 +4737,13 @@ void System::ShutdownDiscordPresence()
|
|||||||
Discord_ClearPresence();
|
Discord_ClearPresence();
|
||||||
Discord_Shutdown();
|
Discord_Shutdown();
|
||||||
s_discord_presence_active = false;
|
s_discord_presence_active = false;
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
s_discord_presence_cheevos_string.clear();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::UpdateDiscordPresence(bool rich_presence_only)
|
void System::UpdateDiscordPresence()
|
||||||
{
|
{
|
||||||
if (!s_discord_presence_active)
|
if (!s_discord_presence_active)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
// Update only if RetroAchievements rich presence has changed
|
|
||||||
const std::string& new_rich_presence = Achievements::GetRichPresenceString();
|
|
||||||
if (new_rich_presence == s_discord_presence_cheevos_string && rich_presence_only)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
s_discord_presence_cheevos_string = new_rich_presence;
|
|
||||||
#else
|
|
||||||
if (rich_presence_only)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// https://discord.com/developers/docs/rich-presence/how-to#updating-presence-update-presence-payload-fields
|
// https://discord.com/developers/docs/rich-presence/how-to#updating-presence-update-presence-payload-fields
|
||||||
DiscordRichPresence rp = {};
|
DiscordRichPresence rp = {};
|
||||||
rp.largeImageKey = "duckstation_logo";
|
rp.largeImageKey = "duckstation_logo";
|
||||||
@ -4827,22 +4760,19 @@ void System::UpdateDiscordPresence(bool rich_presence_only)
|
|||||||
details_string.AppendString("No Game Running");
|
details_string.AppendString("No Game Running");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
SmallString state_string;
|
SmallString state_string;
|
||||||
// Trim to 128 bytes as per Discord-RPC requirements
|
|
||||||
if (s_discord_presence_cheevos_string.length() >= 128)
|
|
||||||
{
|
|
||||||
// 124 characters + 3 dots + null terminator
|
|
||||||
state_string = s_discord_presence_cheevos_string.substr(0, 124);
|
|
||||||
state_string.AppendString("...");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state_string = s_discord_presence_cheevos_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
rp.state = state_string;
|
if (Achievements::HasRichPresence())
|
||||||
#endif
|
{
|
||||||
|
const auto lock = Achievements::GetLock();
|
||||||
|
const std::string_view richp = Achievements::GetRichPresenceString();
|
||||||
|
if (richp.length() >= 128)
|
||||||
|
state_string.AppendFmtString("{}...", richp.substr(0, 124));
|
||||||
|
else
|
||||||
|
state_string.Assign(richp);
|
||||||
|
|
||||||
|
rp.state = state_string;
|
||||||
|
}
|
||||||
rp.details = details_string;
|
rp.details = details_string;
|
||||||
|
|
||||||
Discord_UpdatePresence(&rp);
|
Discord_UpdatePresence(&rp);
|
||||||
@ -4853,8 +4783,6 @@ void System::PollDiscordPresence()
|
|||||||
if (!s_discord_presence_active)
|
if (!s_discord_presence_active)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
UpdateDiscordPresence(true);
|
|
||||||
|
|
||||||
Discord_RunCallbacks();
|
Discord_RunCallbacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,6 +479,11 @@ void UpdateMemorySaveStateSettings();
|
|||||||
bool LoadRewindState(u32 skip_saves = 0, bool consume_state = true);
|
bool LoadRewindState(u32 skip_saves = 0, bool consume_state = true);
|
||||||
void SetRunaheadReplayFlag();
|
void SetRunaheadReplayFlag();
|
||||||
|
|
||||||
|
#ifdef WITH_DISCORD_PRESENCE
|
||||||
|
/// Called when rich presence changes.
|
||||||
|
void UpdateDiscordPresence();
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
/// Called on process startup.
|
/// Called on process startup.
|
||||||
void ProcessStartup();
|
void ProcessStartup();
|
||||||
|
@ -751,11 +751,26 @@ void Host::OnGameChanged(const std::string& disc_path, const std::string& game_s
|
|||||||
NoGUIHost::UpdateWindowTitle(game_name);
|
NoGUIHost::UpdateWindowTitle(game_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Host::OnAchievementsLoginRequested(Achievements::LoginRequestReason reason)
|
||||||
|
{
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::OnAchievementsLoginSuccess(const char* username, u32 points, u32 sc_points, u32 unread_messages)
|
||||||
|
{
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
void Host::OnAchievementsRefreshed()
|
void Host::OnAchievementsRefreshed()
|
||||||
{
|
{
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Host::OnAchievementsHardcoreModeChanged()
|
||||||
|
{
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
void Host::SetMouseMode(bool relative, bool hide_cursor)
|
void Host::SetMouseMode(bool relative, bool hide_cursor)
|
||||||
{
|
{
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -1,16 +1,29 @@
|
|||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#include "achievementlogindialog.h"
|
#include "achievementlogindialog.h"
|
||||||
#include "core/achievements.h"
|
|
||||||
#include "qthost.h"
|
#include "qthost.h"
|
||||||
|
|
||||||
|
#include "core/achievements.h"
|
||||||
|
|
||||||
|
#include "common/error.h"
|
||||||
|
|
||||||
#include <QtWidgets/QMessageBox>
|
#include <QtWidgets/QMessageBox>
|
||||||
|
|
||||||
AchievementLoginDialog::AchievementLoginDialog(QWidget* parent) : QDialog(parent)
|
AchievementLoginDialog::AchievementLoginDialog(QWidget* parent, Achievements::LoginRequestReason reason)
|
||||||
|
: QDialog(parent)
|
||||||
{
|
{
|
||||||
m_ui.setupUi(this);
|
m_ui.setupUi(this);
|
||||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
|
||||||
|
// Adjust text if needed based on reason.
|
||||||
|
if (reason == Achievements::LoginRequestReason::TokenInvalid)
|
||||||
|
{
|
||||||
|
m_ui.instructionText->setText(tr("<strong>Your RetroAchievements login token is no longer valid.</strong> You must "
|
||||||
|
"re-enter your credentials for achievements to be tracked. Your password will not "
|
||||||
|
"be saved in DuckStation, an access token will be generated and used instead."));
|
||||||
|
}
|
||||||
|
|
||||||
m_login = m_ui.buttonBox->addButton(tr("&Login"), QDialogButtonBox::AcceptRole);
|
m_login = m_ui.buttonBox->addButton(tr("&Login"), QDialogButtonBox::AcceptRole);
|
||||||
m_login->setEnabled(false);
|
m_login->setEnabled(false);
|
||||||
connectUi();
|
connectUi();
|
||||||
@ -28,8 +41,11 @@ void AchievementLoginDialog::loginClicked()
|
|||||||
enableUI(false);
|
enableUI(false);
|
||||||
|
|
||||||
Host::RunOnCPUThread([this, username, password]() {
|
Host::RunOnCPUThread([this, username, password]() {
|
||||||
const bool result = Achievements::Login(username.toStdString().c_str(), password.toStdString().c_str());
|
Error error;
|
||||||
QMetaObject::invokeMethod(this, "processLoginResult", Qt::QueuedConnection, Q_ARG(bool, result));
|
const bool result = Achievements::Login(username.toUtf8().constData(), password.toUtf8().constData(), &error);
|
||||||
|
const QString message = QString::fromStdString(error.GetDescription());
|
||||||
|
QMetaObject::invokeMethod(this, "processLoginResult", Qt::QueuedConnection, Q_ARG(bool, result),
|
||||||
|
Q_ARG(const QString&, message));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,12 +54,13 @@ void AchievementLoginDialog::cancelClicked()
|
|||||||
done(1);
|
done(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementLoginDialog::processLoginResult(bool result)
|
void AchievementLoginDialog::processLoginResult(bool result, const QString& message)
|
||||||
{
|
{
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, tr("Login Error"),
|
QMessageBox::critical(
|
||||||
tr("Login failed. Please check your username and password, and try again."));
|
this, tr("Login Error"),
|
||||||
|
tr("Login failed.\nError: %1\n\nPlease check your username and password, and try again.").arg(message));
|
||||||
m_ui.status->setText(tr("Login failed."));
|
m_ui.status->setText(tr("Login failed."));
|
||||||
enableUI(true);
|
enableUI(true);
|
||||||
return;
|
return;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@ -6,18 +6,22 @@
|
|||||||
#include <QtWidgets/QDialog>
|
#include <QtWidgets/QDialog>
|
||||||
#include <QtWidgets/QPushButton>
|
#include <QtWidgets/QPushButton>
|
||||||
|
|
||||||
|
namespace Achievements {
|
||||||
|
enum class LoginRequestReason;
|
||||||
|
}
|
||||||
|
|
||||||
class AchievementLoginDialog : public QDialog
|
class AchievementLoginDialog : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AchievementLoginDialog(QWidget* parent);
|
AchievementLoginDialog(QWidget* parent, Achievements::LoginRequestReason reason);
|
||||||
~AchievementLoginDialog();
|
~AchievementLoginDialog();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void loginClicked();
|
void loginClicked();
|
||||||
void cancelClicked();
|
void cancelClicked();
|
||||||
void processLoginResult(bool result);
|
void processLoginResult(bool result, const QString& message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void connectUi();
|
void connectUi();
|
||||||
|
@ -64,9 +64,9 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QLabel" name="instructionText">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Please enter user name and password for retroachievements.org below. Your password will not be saved in DuckStation, an access token will be generated and used instead.</string>
|
<string>Please enter your user name and password for retroachievements.org below. Your password will not be saved in DuckStation, an access token will be generated and used instead.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -24,54 +24,64 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWi
|
|||||||
m_ui.setupUi(this);
|
m_ui.setupUi(this);
|
||||||
|
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enable, "Cheevos", "Enabled", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enable, "Cheevos", "Enabled", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.richPresence, "Cheevos", "RichPresence", true);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hardcoreMode, "Cheevos", "ChallengeMode", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.challengeMode, "Cheevos", "ChallengeMode", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.achievementNotifications, "Cheevos", "Notifications", true);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.testMode, "Cheevos", "TestMode", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.leaderboardNotifications, "Cheevos",
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.unofficialTestMode, "Cheevos", "UnofficialTestMode", false);
|
"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.useFirstDiscFromPlaylist, "Cheevos",
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useFirstDiscFromPlaylist, "Cheevos",
|
||||||
"UseFirstDiscFromPlaylist", true);
|
"UseFirstDiscFromPlaylist", true);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.leaderboards, "Cheevos", "Leaderboards", true);
|
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.achievementNotificationsDuration, "Cheevos",
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.notifications, "Cheevos", "Notifications", true);
|
"NotificationsDuration",
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.soundEffects, "Cheevos", "SoundEffects", true);
|
Settings::DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.primedIndicators, "Cheevos", "PrimedIndicators", true);
|
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.leaderboardNotificationsDuration, "Cheevos",
|
||||||
|
"LeaderboardsDuration",
|
||||||
|
Settings::DEFAULT_LEADERBOARD_NOTIFICATION_TIME);
|
||||||
|
|
||||||
dialog->registerWidgetHelp(m_ui.enable, tr("Enable Achievements"), tr("Unchecked"),
|
dialog->registerWidgetHelp(m_ui.enable, tr("Enable Achievements"), tr("Unchecked"),
|
||||||
tr("When enabled and logged in, DuckStation will scan for achievements on startup."));
|
tr("When enabled and logged in, DuckStation will scan for achievements on startup."));
|
||||||
dialog->registerWidgetHelp(m_ui.testMode, tr("Enable Test Mode"), tr("Unchecked"),
|
dialog->registerWidgetHelp(m_ui.hardcoreMode, tr("Enable Hardcore Mode"), tr("Unchecked"),
|
||||||
tr("When enabled, DuckStation will assume all achievements are locked and not send any "
|
|
||||||
"unlock notifications to the server."));
|
|
||||||
dialog->registerWidgetHelp(
|
|
||||||
m_ui.unofficialTestMode, 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.richPresence, tr("Enable Rich Presence"), tr("Unchecked"),
|
|
||||||
tr("When enabled, rich presence information will be collected and sent to the server where supported."));
|
|
||||||
dialog->registerWidgetHelp(
|
|
||||||
m_ui.useFirstDiscFromPlaylist, tr("Use First Disc From Playlist"), tr("Unchecked"),
|
|
||||||
tr(
|
|
||||||
"When enabled, the first disc in a playlist will be used for achievements, regardless of which disc is active."));
|
|
||||||
dialog->registerWidgetHelp(m_ui.challengeMode, tr("Enable Hardcore Mode"), tr("Unchecked"),
|
|
||||||
tr("\"Challenge\" mode for achievements, including leaderboard tracking. Disables save "
|
tr("\"Challenge\" mode for achievements, including leaderboard tracking. Disables save "
|
||||||
"state, cheats, and slowdown functions."));
|
"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(
|
dialog->registerWidgetHelp(
|
||||||
m_ui.notifications, tr("Show Notifications"), tr("Checked"),
|
m_ui.leaderboardNotifications, tr("Show Leaderboard Notifications"), tr("Checked"),
|
||||||
tr("Displays popup messages on events such as achievement unlocks and leaderboard submissions."));
|
tr("Displays popup messages when starting, submitting, or failing a leaderboard challenge."));
|
||||||
dialog->registerWidgetHelp(
|
dialog->registerWidgetHelp(
|
||||||
m_ui.soundEffects, tr("Enable Sound Effects"), tr("Checked"),
|
m_ui.soundEffects, tr("Enable Sound Effects"), tr("Checked"),
|
||||||
tr("Plays sound effects for events such as achievement unlocks and leaderboard submissions."));
|
tr("Plays sound effects for events such as achievement unlocks and leaderboard submissions."));
|
||||||
dialog->registerWidgetHelp(
|
dialog->registerWidgetHelp(
|
||||||
m_ui.leaderboards, tr("Enable Leaderboards"), tr("Checked"),
|
m_ui.overlays, tr("Enable In-Game Overlays"), tr("Checked"),
|
||||||
tr("Enables tracking and submission of leaderboards in supported games. If leaderboards "
|
|
||||||
"are disabled, you will still be able to view the leaderboard and scores, but no scores will be uploaded."));
|
|
||||||
dialog->registerWidgetHelp(
|
|
||||||
m_ui.primedIndicators, tr("Show Challenge Indicators"), tr("Checked"),
|
|
||||||
tr("Shows icons in the lower-right corner of the screen when a challenge/primed achievement is active."));
|
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, DuckStation 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, 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.useFirstDiscFromPlaylist, tr("Use First Disc From Playlist"), tr("Unchecked"),
|
||||||
|
tr(
|
||||||
|
"When enabled, the first disc in a playlist will be used for achievements, regardless of which disc is active."));
|
||||||
|
|
||||||
connect(m_ui.enable, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
|
connect(m_ui.enable, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
|
||||||
connect(m_ui.notifications, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
|
connect(m_ui.hardcoreMode, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
|
||||||
connect(m_ui.challengeMode, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
|
connect(m_ui.hardcoreMode, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::onHardcoreModeStateChanged);
|
||||||
connect(m_ui.challengeMode, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::onChallengeModeStateChanged);
|
connect(m_ui.achievementNotifications, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
|
||||||
|
connect(m_ui.leaderboardNotifications, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
|
||||||
|
connect(m_ui.achievementNotificationsDuration, &QSlider::valueChanged, this,
|
||||||
|
&AchievementSettingsWidget::onAchievementsNotificationDurationSliderChanged);
|
||||||
|
connect(m_ui.leaderboardNotificationsDuration, &QSlider::valueChanged, this,
|
||||||
|
&AchievementSettingsWidget::onLeaderboardsNotificationDurationSliderChanged);
|
||||||
|
|
||||||
if (!m_dialog->isPerGameSettings())
|
if (!m_dialog->isPerGameSettings())
|
||||||
{
|
{
|
||||||
@ -95,6 +105,8 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWi
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateEnableState();
|
updateEnableState();
|
||||||
|
onAchievementsNotificationDurationSliderChanged();
|
||||||
|
onLeaderboardsNotificationDurationSliderChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
AchievementSettingsWidget::~AchievementSettingsWidget() = default;
|
AchievementSettingsWidget::~AchievementSettingsWidget() = default;
|
||||||
@ -102,19 +114,24 @@ AchievementSettingsWidget::~AchievementSettingsWidget() = default;
|
|||||||
void AchievementSettingsWidget::updateEnableState()
|
void AchievementSettingsWidget::updateEnableState()
|
||||||
{
|
{
|
||||||
const bool enabled = m_dialog->getEffectiveBoolValue("Cheevos", "Enabled", false);
|
const bool enabled = m_dialog->getEffectiveBoolValue("Cheevos", "Enabled", false);
|
||||||
const bool challenge = m_dialog->getEffectiveBoolValue("Cheevos", "ChallengeMode", false);
|
const bool notifications = enabled && m_dialog->getEffectiveBoolValue("Cheevos", "Notifications", true);
|
||||||
m_ui.testMode->setEnabled(enabled);
|
const bool lb_notifications = enabled && m_dialog->getEffectiveBoolValue("Cheevos", "LeaderboardNotifications", true);
|
||||||
m_ui.useFirstDiscFromPlaylist->setEnabled(enabled);
|
m_ui.hardcoreMode->setEnabled(enabled);
|
||||||
m_ui.richPresence->setEnabled(enabled);
|
m_ui.achievementNotifications->setEnabled(enabled);
|
||||||
m_ui.challengeMode->setEnabled(enabled);
|
m_ui.leaderboardNotifications->setEnabled(enabled);
|
||||||
m_ui.leaderboards->setEnabled(enabled && challenge);
|
m_ui.achievementNotificationsDuration->setEnabled(notifications);
|
||||||
m_ui.unofficialTestMode->setEnabled(enabled);
|
m_ui.achievementNotificationsDurationLabel->setEnabled(notifications);
|
||||||
m_ui.notifications->setEnabled(enabled);
|
m_ui.leaderboardNotificationsDuration->setEnabled(lb_notifications);
|
||||||
|
m_ui.leaderboardNotificationsDurationLabel->setEnabled(lb_notifications);
|
||||||
m_ui.soundEffects->setEnabled(enabled);
|
m_ui.soundEffects->setEnabled(enabled);
|
||||||
m_ui.primedIndicators->setEnabled(enabled);
|
m_ui.overlays->setEnabled(enabled);
|
||||||
|
m_ui.encoreMode->setEnabled(enabled);
|
||||||
|
m_ui.spectatorMode->setEnabled(enabled);
|
||||||
|
m_ui.unofficialAchievements->setEnabled(enabled);
|
||||||
|
m_ui.useFirstDiscFromPlaylist->setEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementSettingsWidget::onChallengeModeStateChanged()
|
void AchievementSettingsWidget::onHardcoreModeStateChanged()
|
||||||
{
|
{
|
||||||
if (!QtHost::IsSystemValid())
|
if (!QtHost::IsSystemValid())
|
||||||
return;
|
return;
|
||||||
@ -140,6 +157,20 @@ void AchievementSettingsWidget::onChallengeModeStateChanged()
|
|||||||
g_emu_thread->resetSystem();
|
g_emu_thread->resetSystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AchievementSettingsWidget::onAchievementsNotificationDurationSliderChanged()
|
||||||
|
{
|
||||||
|
const float duration = m_dialog->getEffectiveFloatValue("Cheevos", "NotificationsDuration",
|
||||||
|
Settings::DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME);
|
||||||
|
m_ui.achievementNotificationsDurationLabel->setText(tr("%n seconds", nullptr, static_cast<int>(duration)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AchievementSettingsWidget::onLeaderboardsNotificationDurationSliderChanged()
|
||||||
|
{
|
||||||
|
const float duration = m_dialog->getEffectiveFloatValue("Cheevos", "LeaderboardsDuration",
|
||||||
|
Settings::DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME);
|
||||||
|
m_ui.leaderboardNotificationsDurationLabel->setText(tr("%n seconds", nullptr, static_cast<int>(duration)));
|
||||||
|
}
|
||||||
|
|
||||||
void AchievementSettingsWidget::updateLoginState()
|
void AchievementSettingsWidget::updateLoginState()
|
||||||
{
|
{
|
||||||
const std::string username(Host::GetBaseStringSettingValue("Cheevos", "Username"));
|
const std::string username(Host::GetBaseStringSettingValue("Cheevos", "Username"));
|
||||||
@ -173,7 +204,7 @@ void AchievementSettingsWidget::onLoginLogoutPressed()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AchievementLoginDialog login(this);
|
AchievementLoginDialog login(this, Achievements::LoginRequestReason::UserInitiated);
|
||||||
int res = login.exec();
|
int res = login.exec();
|
||||||
if (res != 0)
|
if (res != 0)
|
||||||
return;
|
return;
|
||||||
@ -193,8 +224,7 @@ void AchievementSettingsWidget::onViewProfilePressed()
|
|||||||
QUrl(QStringLiteral("https://retroachievements.org/user/%1").arg(QString::fromUtf8(encoded_username))));
|
QUrl(QStringLiteral("https://retroachievements.org/user/%1").arg(QString::fromUtf8(encoded_username))));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementSettingsWidget::onAchievementsRefreshed(quint32 id, const QString& game_info_string, quint32 total,
|
void AchievementSettingsWidget::onAchievementsRefreshed(quint32 id, const QString& game_info_string)
|
||||||
quint32 points)
|
|
||||||
{
|
{
|
||||||
m_ui.gameInfo->setText(game_info_string);
|
m_ui.gameInfo->setText(game_info_string);
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,12 @@ public:
|
|||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void updateEnableState();
|
void updateEnableState();
|
||||||
void onChallengeModeStateChanged();
|
void onHardcoreModeStateChanged();
|
||||||
|
void onAchievementsNotificationDurationSliderChanged();
|
||||||
|
void onLeaderboardsNotificationDurationSliderChanged();
|
||||||
void onLoginLogoutPressed();
|
void onLoginLogoutPressed();
|
||||||
void onViewProfilePressed();
|
void onViewProfilePressed();
|
||||||
void onAchievementsRefreshed(quint32 id, const QString& game_info_string, quint32 total, quint32 points);
|
void onAchievementsRefreshed(quint32 id, const QString& game_info_string);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateLoginState();
|
void updateLoginState();
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>648</width>
|
<width>674</width>
|
||||||
<height>475</height>
|
<height>481</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -29,20 +29,13 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox">
|
<widget class="QGroupBox" name="groupBox">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Global Settings</string>
|
<string>Settings</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QCheckBox" name="richPresence">
|
|
||||||
<property name="text">
|
|
||||||
<string>Enable Rich Presence</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QCheckBox" name="leaderboards">
|
<widget class="QCheckBox" name="spectatorMode">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Leaderboards</string>
|
<string>Enable Spectator Mode</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -53,52 +46,146 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QCheckBox" name="unofficialAchievements">
|
||||||
|
<property name="text">
|
||||||
|
<string>Test Unofficial Achievements</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QCheckBox" name="challengeMode">
|
<widget class="QCheckBox" name="encoreMode">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable Encore Mode</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QCheckBox" name="hardcoreMode">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Hardcore Mode</string>
|
<string>Enable Hardcore Mode</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="2" column="1">
|
||||||
<widget class="QCheckBox" name="primedIndicators">
|
|
||||||
<property name="text">
|
|
||||||
<string>Show Challenge Indicators</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QCheckBox" name="useFirstDiscFromPlaylist">
|
<widget class="QCheckBox" name="useFirstDiscFromPlaylist">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use First Disc From Playlist</string>
|
<string>Use First Disc From Playlist</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
</layout>
|
||||||
<widget class="QCheckBox" name="testMode">
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="notificationBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Notifications</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2" columnstretch="1,1">
|
||||||
|
<item row="0" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="1,0">
|
||||||
|
<item>
|
||||||
|
<widget class="QSlider" name="achievementNotificationsDuration">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>30</number>
|
||||||
|
</property>
|
||||||
|
<property name="pageStep">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="invertedAppearance">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="tickPosition">
|
||||||
|
<enum>QSlider::TicksBelow</enum>
|
||||||
|
</property>
|
||||||
|
<property name="tickInterval">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="achievementNotificationsDurationLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>5 seconds</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QCheckBox" name="achievementNotifications">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Test Mode</string>
|
<string>Show Achievement Notifications</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QCheckBox" name="unofficialTestMode">
|
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0">
|
||||||
|
<item>
|
||||||
|
<widget class="QSlider" name="leaderboardNotificationsDuration">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>30</number>
|
||||||
|
</property>
|
||||||
|
<property name="pageStep">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="invertedAppearance">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="tickPosition">
|
||||||
|
<enum>QSlider::TicksBelow</enum>
|
||||||
|
</property>
|
||||||
|
<property name="tickInterval">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="leaderboardNotificationsDurationLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>5 seconds</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="leaderboardNotifications">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Test Unofficial Achievements</string>
|
<string>Show Leaderboard Notifications</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="2" column="0">
|
||||||
<widget class="QCheckBox" name="soundEffects">
|
<widget class="QCheckBox" name="soundEffects">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Sound Effects</string>
|
<string>Enable Sound Effects</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="2" column="1">
|
||||||
<widget class="QCheckBox" name="notifications">
|
<widget class="QCheckBox" name="overlays">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Show Notifications</string>
|
<string>Enable In-Game Overlays</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -113,20 +200,17 @@
|
|||||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
|
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="loginStatus">
|
<widget class="QLabel" name="loginStatus">
|
||||||
|
<property name="text">
|
||||||
|
<string>Username:
|
||||||
|
Login token generated at:</string>
|
||||||
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="loginButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>Login...</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="viewProfile">
|
<widget class="QPushButton" name="viewProfile">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -134,6 +218,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="loginButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Login...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -144,7 +235,7 @@
|
|||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>0</width>
|
<width>0</width>
|
||||||
<height>120</height>
|
<height>75</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="title">
|
<property name="title">
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "aboutdialog.h"
|
#include "aboutdialog.h"
|
||||||
|
#include "achievementlogindialog.h"
|
||||||
#include "autoupdaterdialog.h"
|
#include "autoupdaterdialog.h"
|
||||||
#include "cheatmanagerdialog.h"
|
#include "cheatmanagerdialog.h"
|
||||||
#include "coverdownloaddialog.h"
|
#include "coverdownloaddialog.h"
|
||||||
@ -507,14 +508,14 @@ void MainWindow::onSystemStarting()
|
|||||||
s_system_valid = false;
|
s_system_valid = false;
|
||||||
s_system_paused = false;
|
s_system_paused = false;
|
||||||
|
|
||||||
updateEmulationActions(true, false, Achievements::ChallengeModeActive());
|
updateEmulationActions(true, false, Achievements::IsHardcoreModeActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onSystemStarted()
|
void MainWindow::onSystemStarted()
|
||||||
{
|
{
|
||||||
m_was_disc_change_request = false;
|
m_was_disc_change_request = false;
|
||||||
s_system_valid = true;
|
s_system_valid = true;
|
||||||
updateEmulationActions(false, true, Achievements::ChallengeModeActive());
|
updateEmulationActions(false, true, Achievements::IsHardcoreModeActive());
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
updateStatusBarWidgetVisibility();
|
updateStatusBarWidgetVisibility();
|
||||||
}
|
}
|
||||||
@ -572,7 +573,7 @@ void MainWindow::onSystemDestroyed()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateEmulationActions(false, false, Achievements::ChallengeModeActive());
|
updateEmulationActions(false, false, Achievements::IsHardcoreModeActive());
|
||||||
if (m_display_widget)
|
if (m_display_widget)
|
||||||
updateDisplayWidgetCursor();
|
updateDisplayWidgetCursor();
|
||||||
else
|
else
|
||||||
@ -724,7 +725,7 @@ void MainWindow::populateGameListContextMenu(const GameList::Entry* entry, QWidg
|
|||||||
{
|
{
|
||||||
std::vector<SaveStateInfo> available_states(System::GetAvailableSaveStates(entry->serial.c_str()));
|
std::vector<SaveStateInfo> available_states(System::GetAvailableSaveStates(entry->serial.c_str()));
|
||||||
const QString timestamp_format = QLocale::system().dateTimeFormat(QLocale::ShortFormat);
|
const QString timestamp_format = QLocale::system().dateTimeFormat(QLocale::ShortFormat);
|
||||||
const bool challenge_mode = Achievements::ChallengeModeActive();
|
const bool challenge_mode = Achievements::IsHardcoreModeActive();
|
||||||
for (SaveStateInfo& ssi : available_states)
|
for (SaveStateInfo& ssi : available_states)
|
||||||
{
|
{
|
||||||
if (ssi.global)
|
if (ssi.global)
|
||||||
@ -1382,7 +1383,7 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
|
|||||||
g_emu_thread->bootSystem(std::move(boot_params));
|
g_emu_thread->bootSystem(std::move(boot_params));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (m_ui.menuDebug->menuAction()->isVisible() && !Achievements::ChallengeModeActive())
|
if (m_ui.menuDebug->menuAction()->isVisible() && !Achievements::IsHardcoreModeActive())
|
||||||
{
|
{
|
||||||
connect(menu.addAction(tr("Boot and Debug")), &QAction::triggered, [this, entry]() {
|
connect(menu.addAction(tr("Boot and Debug")), &QAction::triggered, [this, entry]() {
|
||||||
m_open_debugger_on_start = true;
|
m_open_debugger_on_start = true;
|
||||||
@ -1874,7 +1875,7 @@ void MainWindow::switchToEmulationView()
|
|||||||
|
|
||||||
void MainWindow::connectSignals()
|
void MainWindow::connectSignals()
|
||||||
{
|
{
|
||||||
updateEmulationActions(false, false, Achievements::ChallengeModeActive());
|
updateEmulationActions(false, false, Achievements::IsHardcoreModeActive());
|
||||||
|
|
||||||
connect(qApp, &QGuiApplication::applicationStateChanged, this, &MainWindow::onApplicationStateChanged);
|
connect(qApp, &QGuiApplication::applicationStateChanged, this, &MainWindow::onApplicationStateChanged);
|
||||||
|
|
||||||
@ -1980,10 +1981,10 @@ void MainWindow::connectSignals()
|
|||||||
connect(g_emu_thread, &EmuThread::runningGameChanged, this, &MainWindow::onRunningGameChanged);
|
connect(g_emu_thread, &EmuThread::runningGameChanged, this, &MainWindow::onRunningGameChanged);
|
||||||
connect(g_emu_thread, &EmuThread::mouseModeRequested, this, &MainWindow::onMouseModeRequested);
|
connect(g_emu_thread, &EmuThread::mouseModeRequested, this, &MainWindow::onMouseModeRequested);
|
||||||
connect(g_emu_thread, &EmuThread::fullscreenUIStateChange, this, &MainWindow::onFullscreenUIStateChange);
|
connect(g_emu_thread, &EmuThread::fullscreenUIStateChange, this, &MainWindow::onFullscreenUIStateChange);
|
||||||
#ifdef WITH_CHEEVOS
|
connect(g_emu_thread, &EmuThread::achievementsLoginRequested, this, &MainWindow::onAchievementsLoginRequested);
|
||||||
|
connect(g_emu_thread, &EmuThread::achievementsLoginSucceeded, this, &MainWindow::onAchievementsLoginSucceeded);
|
||||||
connect(g_emu_thread, &EmuThread::achievementsChallengeModeChanged, this,
|
connect(g_emu_thread, &EmuThread::achievementsChallengeModeChanged, this,
|
||||||
&MainWindow::onAchievementsChallengeModeChanged);
|
&MainWindow::onAchievementsChallengeModeChanged);
|
||||||
#endif
|
|
||||||
|
|
||||||
// These need to be queued connections to stop crashing due to menus opening/closing and switching focus.
|
// These need to be queued connections to stop crashing due to menus opening/closing and switching focus.
|
||||||
connect(m_game_list_widget, &GameListWidget::refreshProgress, this, &MainWindow::onGameListRefreshProgress);
|
connect(m_game_list_widget, &GameListWidget::refreshProgress, this, &MainWindow::onGameListRefreshProgress);
|
||||||
@ -2619,10 +2620,28 @@ void MainWindow::openMemoryCardEditor(const QString& card_a_path, const QString&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::onAchievementsLoginRequested(Achievements::LoginRequestReason reason)
|
||||||
|
{
|
||||||
|
const auto lock = pauseAndLockSystem();
|
||||||
|
|
||||||
|
AchievementLoginDialog dlg(lock.getDialogParent(), reason);
|
||||||
|
dlg.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onAchievementsLoginSucceeded(const QString& display_name, quint32 points, quint32 sc_points,
|
||||||
|
quint32 unread_messages)
|
||||||
|
{
|
||||||
|
const QString message = tr("RA: Logged in as %1 (%2, %3 softcore). %4 unread messages.")
|
||||||
|
.arg(display_name)
|
||||||
|
.arg(points)
|
||||||
|
.arg(sc_points)
|
||||||
|
.arg(unread_messages);
|
||||||
|
m_ui.statusBar->showMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::onAchievementsChallengeModeChanged()
|
void MainWindow::onAchievementsChallengeModeChanged()
|
||||||
{
|
{
|
||||||
#ifdef WITH_CHEEVOS
|
const bool active = Achievements::IsHardcoreModeActive();
|
||||||
const bool active = Achievements::ChallengeModeActive();
|
|
||||||
if (active)
|
if (active)
|
||||||
{
|
{
|
||||||
if (m_cheat_manager_dialog)
|
if (m_cheat_manager_dialog)
|
||||||
@ -2641,7 +2660,6 @@ void MainWindow::onAchievementsChallengeModeChanged()
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateEmulationActions(false, System::IsValid(), active);
|
updateEmulationActions(false, System::IsValid(), active);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onToolsMemoryCardEditorTriggered()
|
void MainWindow::onToolsMemoryCardEditorTriggered()
|
||||||
|
@ -33,6 +33,9 @@ class DebuggerWindow;
|
|||||||
class MainWindow;
|
class MainWindow;
|
||||||
|
|
||||||
class GPUDevice;
|
class GPUDevice;
|
||||||
|
namespace Achievements {
|
||||||
|
enum class LoginRequestReason;
|
||||||
|
}
|
||||||
namespace GameList {
|
namespace GameList {
|
||||||
struct Entry;
|
struct Entry;
|
||||||
}
|
}
|
||||||
@ -128,6 +131,9 @@ private Q_SLOTS:
|
|||||||
void onSystemPaused();
|
void onSystemPaused();
|
||||||
void onSystemResumed();
|
void onSystemResumed();
|
||||||
void onRunningGameChanged(const QString& filename, const QString& game_serial, const QString& game_title);
|
void onRunningGameChanged(const QString& filename, const QString& game_serial, const QString& game_title);
|
||||||
|
void onAchievementsLoginRequested(Achievements::LoginRequestReason reason);
|
||||||
|
void onAchievementsLoginSucceeded(const QString& display_name, quint32 points, quint32 sc_points,
|
||||||
|
quint32 unread_messages);
|
||||||
void onAchievementsChallengeModeChanged();
|
void onAchievementsChallengeModeChanged();
|
||||||
void onApplicationStateChanged(Qt::ApplicationState state);
|
void onApplicationStateChanged(Qt::ApplicationState state);
|
||||||
|
|
||||||
|
@ -1204,32 +1204,33 @@ void EmuThread::saveScreenshot()
|
|||||||
System::SaveScreenshot(nullptr, true, true);
|
System::SaveScreenshot(nullptr, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Host::OnAchievementsLoginRequested(Achievements::LoginRequestReason reason)
|
||||||
|
{
|
||||||
|
emit g_emu_thread->achievementsLoginRequested(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::OnAchievementsLoginSuccess(const char* username, u32 points, u32 sc_points, u32 unread_messages)
|
||||||
|
{
|
||||||
|
emit g_emu_thread->achievementsLoginSucceeded(QString::fromUtf8(username), points, sc_points, unread_messages);
|
||||||
|
}
|
||||||
|
|
||||||
void Host::OnAchievementsRefreshed()
|
void Host::OnAchievementsRefreshed()
|
||||||
{
|
{
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
u32 game_id = 0;
|
u32 game_id = 0;
|
||||||
u32 achievement_count = 0;
|
|
||||||
u32 max_points = 0;
|
|
||||||
|
|
||||||
QString game_info;
|
QString game_info;
|
||||||
|
|
||||||
if (Achievements::HasActiveGame())
|
if (Achievements::HasActiveGame())
|
||||||
{
|
{
|
||||||
game_id = Achievements::GetGameID();
|
game_id = Achievements::GetGameID();
|
||||||
achievement_count = Achievements::GetAchievementCount();
|
|
||||||
max_points = Achievements::GetMaximumPointsForGame();
|
|
||||||
|
|
||||||
game_info = qApp
|
game_info = qApp
|
||||||
->translate("EmuThread", "Game ID: %1\n"
|
->translate("EmuThread", "Game: %1 (%2)\n")
|
||||||
"Game Title: %2\n"
|
.arg(QString::fromStdString(Achievements::GetGameTitle()))
|
||||||
"Achievements: %5 (%6)\n\n")
|
.arg(game_id);
|
||||||
.arg(game_id)
|
|
||||||
.arg(QString::fromStdString(Achievements::GetGameTitle()))
|
|
||||||
.arg(achievement_count)
|
|
||||||
.arg(qApp->translate("EmuThread", "%n points", "", max_points));
|
|
||||||
|
|
||||||
const std::string rich_presence_string(Achievements::GetRichPresenceString());
|
const std::string& rich_presence_string = Achievements::GetRichPresenceString();
|
||||||
if (!rich_presence_string.empty())
|
if (Achievements::HasRichPresence() && !rich_presence_string.empty())
|
||||||
game_info.append(QString::fromStdString(rich_presence_string));
|
game_info.append(QString::fromStdString(rich_presence_string));
|
||||||
else
|
else
|
||||||
game_info.append(qApp->translate("EmuThread", "Rich presence inactive or unsupported."));
|
game_info.append(qApp->translate("EmuThread", "Rich presence inactive or unsupported."));
|
||||||
@ -1239,15 +1240,12 @@ void Host::OnAchievementsRefreshed()
|
|||||||
game_info = qApp->translate("EmuThread", "Game not loaded or no RetroAchievements available.");
|
game_info = qApp->translate("EmuThread", "Game not loaded or no RetroAchievements available.");
|
||||||
}
|
}
|
||||||
|
|
||||||
emit g_emu_thread->achievementsRefreshed(game_id, game_info, achievement_count, max_points);
|
emit g_emu_thread->achievementsRefreshed(game_id, game_info);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::OnAchievementsChallengeModeChanged()
|
void Host::OnAchievementsHardcoreModeChanged()
|
||||||
{
|
{
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
emit g_emu_thread->achievementsChallengeModeChanged();
|
emit g_emu_thread->achievementsChallengeModeChanged();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::doBackgroundControllerPoll()
|
void EmuThread::doBackgroundControllerPoll()
|
||||||
|
@ -45,14 +45,13 @@ class GPUDevice;
|
|||||||
class MainWindow;
|
class MainWindow;
|
||||||
class DisplayWidget;
|
class DisplayWidget;
|
||||||
|
|
||||||
|
namespace Achievements {
|
||||||
|
enum class LoginRequestReason;
|
||||||
|
}
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(std::optional<bool>);
|
Q_DECLARE_METATYPE(std::optional<bool>);
|
||||||
Q_DECLARE_METATYPE(std::shared_ptr<SystemBootParameters>);
|
Q_DECLARE_METATYPE(std::shared_ptr<SystemBootParameters>);
|
||||||
|
|
||||||
// These cause errors when compiling with gcc, implicitly defined?
|
|
||||||
// Q_DECLARE_METATYPE(std::function<void()>);
|
|
||||||
// Q_DECLARE_METATYPE(GPURenderer);
|
|
||||||
// Q_DECLARE_METATYPE(InputBindingKey);
|
|
||||||
|
|
||||||
class EmuThread : public QThread
|
class EmuThread : public QThread
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -141,7 +140,10 @@ Q_SIGNALS:
|
|||||||
void inputProfileLoaded();
|
void inputProfileLoaded();
|
||||||
void mouseModeRequested(bool relative, bool hide_cursor);
|
void mouseModeRequested(bool relative, bool hide_cursor);
|
||||||
void fullscreenUIStateChange(bool running);
|
void fullscreenUIStateChange(bool running);
|
||||||
void achievementsRefreshed(quint32 id, const QString& game_info_string, quint32 total, quint32 points);
|
void achievementsLoginRequested(Achievements::LoginRequestReason reason);
|
||||||
|
void achievementsLoginSucceeded(const QString& display_name, quint32 points, quint32 sc_points,
|
||||||
|
quint32 unread_messages);
|
||||||
|
void achievementsRefreshed(quint32 id, const QString& game_info_string);
|
||||||
void achievementsChallengeModeChanged();
|
void achievementsChallengeModeChanged();
|
||||||
void cheatEnabled(quint32 index, bool enabled);
|
void cheatEnabled(quint32 index, bool enabled);
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#include "settingsdialog.h"
|
#include "settingsdialog.h"
|
||||||
@ -7,6 +7,7 @@
|
|||||||
#include "biossettingswidget.h"
|
#include "biossettingswidget.h"
|
||||||
#include "consolesettingswidget.h"
|
#include "consolesettingswidget.h"
|
||||||
|
|
||||||
|
#include "achievementsettingswidget.h"
|
||||||
#include "displaysettingswidget.h"
|
#include "displaysettingswidget.h"
|
||||||
#include "emulationsettingswidget.h"
|
#include "emulationsettingswidget.h"
|
||||||
#include "enhancementsettingswidget.h"
|
#include "enhancementsettingswidget.h"
|
||||||
@ -19,6 +20,7 @@
|
|||||||
#include "postprocessingsettingswidget.h"
|
#include "postprocessingsettingswidget.h"
|
||||||
#include "qthost.h"
|
#include "qthost.h"
|
||||||
|
|
||||||
|
#include "core/achievements.h"
|
||||||
#include "core/host.h"
|
#include "core/host.h"
|
||||||
|
|
||||||
#include "util/ini_settings_interface.h"
|
#include "util/ini_settings_interface.h"
|
||||||
@ -29,11 +31,6 @@
|
|||||||
#include <QtWidgets/QMessageBox>
|
#include <QtWidgets/QMessageBox>
|
||||||
#include <QtWidgets/QTextEdit>
|
#include <QtWidgets/QTextEdit>
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
#include "achievementsettingswidget.h"
|
|
||||||
#include "core/achievements.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static QList<SettingsDialog*> s_open_game_properties_dialogs;
|
static QList<SettingsDialog*> s_open_game_properties_dialogs;
|
||||||
|
|
||||||
SettingsDialog::SettingsDialog(QWidget* parent) : QDialog(parent)
|
SettingsDialog::SettingsDialog(QWidget* parent) : QDialog(parent)
|
||||||
@ -129,7 +126,6 @@ void SettingsDialog::addPages()
|
|||||||
QString help_text(tr("<strong>Achievement Settings</strong><hr>These options control RetroAchievements. Mouse over "
|
QString help_text(tr("<strong>Achievement Settings</strong><hr>These options control RetroAchievements. Mouse over "
|
||||||
"an option for additional information."));
|
"an option for additional information."));
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
|
||||||
if (!Achievements::IsUsingRAIntegration())
|
if (!Achievements::IsUsingRAIntegration())
|
||||||
{
|
{
|
||||||
addWidget(m_achievement_settings = new AchievementSettingsWidget(this, m_ui.settingsContainer), std::move(title),
|
addWidget(m_achievement_settings = new AchievementSettingsWidget(this, m_ui.settingsContainer), std::move(title),
|
||||||
@ -144,13 +140,6 @@ void SettingsDialog::addPages()
|
|||||||
|
|
||||||
addWidget(placeholder_label, std::move(title), std::move(icon_text), std::move(help_text));
|
addWidget(placeholder_label, std::move(title), std::move(icon_text), std::move(help_text));
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
QLabel* placeholder_label =
|
|
||||||
new QLabel(tr("This DuckStation build was not compiled with RetroAchievements support."), m_ui.settingsContainer);
|
|
||||||
placeholder_label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
|
||||||
|
|
||||||
addWidget(placeholder_label, std::move(title), std::move(icon_text), std::move(help_text));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isPerGameSettings())
|
if (!isPerGameSettings())
|
||||||
|
@ -325,14 +325,25 @@ void Host::SetMouseMode(bool relative, bool hide_cursor)
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WITH_CHEEVOS
|
void Host::OnAchievementsLoginRequested(Achievements::LoginRequestReason reason)
|
||||||
|
{
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::OnAchievementsLoginSuccess(const char* username, u32 points, u32 sc_points, u32 unread_messages)
|
||||||
|
{
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
void Host::OnAchievementsRefreshed()
|
void Host::OnAchievementsRefreshed()
|
||||||
{
|
{
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
void Host::OnAchievementsHardcoreModeChanged()
|
||||||
|
{
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<u32> InputManager::ConvertHostKeyboardStringToCode(const std::string_view& str)
|
std::optional<u32> InputManager::ConvertHostKeyboardStringToCode(const std::string_view& str)
|
||||||
{
|
{
|
||||||
|
@ -15,9 +15,9 @@
|
|||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "common/threading.h"
|
#include "common/threading.h"
|
||||||
#include "common/timer.h"
|
#include "common/timer.h"
|
||||||
#include "gpu_device.h"
|
|
||||||
#include "core/host.h"
|
#include "core/host.h"
|
||||||
#include "fmt/core.h"
|
#include "fmt/core.h"
|
||||||
|
#include "gpu_device.h"
|
||||||
#include "imgui_internal.h"
|
#include "imgui_internal.h"
|
||||||
#include "imgui_stdlib.h"
|
#include "imgui_stdlib.h"
|
||||||
|
|
||||||
@ -140,8 +140,12 @@ static std::string s_file_selector_current_directory;
|
|||||||
static std::vector<std::string> s_file_selector_filters;
|
static std::vector<std::string> s_file_selector_filters;
|
||||||
static std::vector<FileSelectorItem> s_file_selector_items;
|
static std::vector<FileSelectorItem> s_file_selector_items;
|
||||||
|
|
||||||
|
static constexpr float NOTIFICATION_FADE_IN_TIME = 0.2f;
|
||||||
|
static constexpr float NOTIFICATION_FADE_OUT_TIME = 0.8f;
|
||||||
|
|
||||||
struct Notification
|
struct Notification
|
||||||
{
|
{
|
||||||
|
std::string key;
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string text;
|
std::string text;
|
||||||
std::string badge_path;
|
std::string badge_path;
|
||||||
@ -272,7 +276,7 @@ std::shared_ptr<GPUTexture> ImGuiFullscreen::UploadTexture(const char* path, con
|
|||||||
{
|
{
|
||||||
std::unique_ptr<GPUTexture> texture =
|
std::unique_ptr<GPUTexture> texture =
|
||||||
g_gpu_device->CreateTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture,
|
g_gpu_device->CreateTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture,
|
||||||
GPUTexture::Format::RGBA8, image.GetPixels(), image.GetPitch());
|
GPUTexture::Format::RGBA8, image.GetPixels(), image.GetPitch());
|
||||||
if (!texture)
|
if (!texture)
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("failed to create %ux%u texture for resource", image.GetWidth(), image.GetHeight());
|
Log_ErrorPrintf("failed to create %ux%u texture for resource", image.GetWidth(), image.GetHeight());
|
||||||
@ -1614,6 +1618,84 @@ bool ImGuiFullscreen::NavButton(const char* title, bool is_active, bool enabled
|
|||||||
return pressed;
|
return pressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ImGuiFullscreen::NavTab(const char* title, bool is_active, bool enabled /* = true */, float width, float height,
|
||||||
|
const ImVec4& background, ImFont* font /* = g_large_font */)
|
||||||
|
{
|
||||||
|
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||||
|
if (window->SkipItems)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
s_menu_button_index++;
|
||||||
|
|
||||||
|
const ImVec2 text_size(font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), 0.0f, title));
|
||||||
|
const ImVec2 pos(window->DC.CursorPos);
|
||||||
|
const ImVec2 size = ImVec2(((width < 0.0f) ? text_size.x : LayoutScale(width)), LayoutScale(height));
|
||||||
|
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
|
||||||
|
ImGui::ItemSize(ImVec2(size.x, size.y));
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
|
ImRect bb(pos, pos + size);
|
||||||
|
const ImGuiID id = window->GetID(title);
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
// bit contradictory - we don't want this button to be used for *gamepad* navigation, since they're usually
|
||||||
|
// activated with the bumpers and/or the back button.
|
||||||
|
if (!ImGui::ItemAdd(bb, id, nullptr, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ImGui::IsClippedEx(bb, id))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool held;
|
||||||
|
bool pressed;
|
||||||
|
bool hovered;
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pressed = false;
|
||||||
|
held = false;
|
||||||
|
hovered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImU32 col =
|
||||||
|
hovered ? ImGui::GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered, 1.0f) :
|
||||||
|
ImGui::GetColorU32(is_active ? background : ImVec4(background.x, background.y, background.z, 0.5f));
|
||||||
|
|
||||||
|
ImGui::RenderFrame(bb.Min, bb.Max, col, true, 0.0f);
|
||||||
|
|
||||||
|
if (is_active)
|
||||||
|
{
|
||||||
|
const float line_thickness = LayoutScale(2.0f);
|
||||||
|
ImGui::GetWindowDrawList()->AddLine(ImVec2(bb.Min.x, bb.Max.y - line_thickness),
|
||||||
|
ImVec2(bb.Max.x, bb.Max.y - line_thickness),
|
||||||
|
ImGui::GetColorU32(ImGuiCol_TextDisabled), line_thickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImVec2 pad(std::max((size.x - text_size.x) * 0.5f, 0.0f), std::max((size.y - text_size.y) * 0.5f, 0.0f));
|
||||||
|
bb.Min += pad;
|
||||||
|
bb.Max -= pad;
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(
|
||||||
|
ImGuiCol_Text,
|
||||||
|
ImGui::GetColorU32(enabled ? (is_active ? ImGuiCol_Text : ImGuiCol_TextDisabled) : ImGuiCol_ButtonHovered));
|
||||||
|
|
||||||
|
ImGui::PushFont(font);
|
||||||
|
ImGui::RenderTextClipped(bb.Min, bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &bb);
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
return pressed;
|
||||||
|
}
|
||||||
|
|
||||||
void ImGuiFullscreen::PopulateFileSelectorItems()
|
void ImGuiFullscreen::PopulateFileSelectorItems()
|
||||||
{
|
{
|
||||||
s_file_selector_items.clear();
|
s_file_selector_items.clear();
|
||||||
@ -2335,15 +2417,40 @@ void ImGuiFullscreen::DrawBackgroundProgressDialogs(ImVec2& position, float spac
|
|||||||
// Notifications
|
// Notifications
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void ImGuiFullscreen::AddNotification(float duration, std::string title, std::string text, std::string image_path)
|
void ImGuiFullscreen::AddNotification(std::string key, float duration, std::string title, std::string text,
|
||||||
|
std::string image_path)
|
||||||
{
|
{
|
||||||
|
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
|
||||||
|
|
||||||
|
if (!key.empty())
|
||||||
|
{
|
||||||
|
for (auto it = s_notifications.begin(); it != s_notifications.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it->key == key)
|
||||||
|
{
|
||||||
|
it->duration = duration;
|
||||||
|
it->title = std::move(title);
|
||||||
|
it->text = std::move(text);
|
||||||
|
it->badge_path = std::move(image_path);
|
||||||
|
|
||||||
|
// Don't fade it in again
|
||||||
|
const float time_passed =
|
||||||
|
static_cast<float>(Common::Timer::ConvertValueToSeconds(current_time - it->start_time));
|
||||||
|
it->start_time =
|
||||||
|
current_time - Common::Timer::ConvertSecondsToValue(std::min(time_passed, NOTIFICATION_FADE_IN_TIME));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Notification notif;
|
Notification notif;
|
||||||
|
notif.key = std::move(key);
|
||||||
notif.duration = duration;
|
notif.duration = duration;
|
||||||
notif.title = std::move(title);
|
notif.title = std::move(title);
|
||||||
notif.text = std::move(text);
|
notif.text = std::move(text);
|
||||||
notif.badge_path = std::move(image_path);
|
notif.badge_path = std::move(image_path);
|
||||||
notif.start_time = Common::Timer::GetCurrentValue();
|
notif.start_time = current_time;
|
||||||
notif.move_time = notif.start_time;
|
notif.move_time = current_time;
|
||||||
notif.target_y = -1.0f;
|
notif.target_y = -1.0f;
|
||||||
notif.last_y = -1.0f;
|
notif.last_y = -1.0f;
|
||||||
s_notifications.push_back(std::move(notif));
|
s_notifications.push_back(std::move(notif));
|
||||||
@ -2359,8 +2466,6 @@ void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing)
|
|||||||
if (s_notifications.empty())
|
if (s_notifications.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
static constexpr float FADE_IN_TIME = 0.2f;
|
|
||||||
static constexpr float FADE_OUT_TIME = 0.8f;
|
|
||||||
static constexpr float MOVE_DURATION = 0.5f;
|
static constexpr float MOVE_DURATION = 0.5f;
|
||||||
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
|
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
|
||||||
|
|
||||||
@ -2413,10 +2518,10 @@ void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing)
|
|||||||
std::max((vertical_padding * 2.0f) + title_size.y + vertical_spacing + text_size.y, min_height);
|
std::max((vertical_padding * 2.0f) + title_size.y + vertical_spacing + text_size.y, min_height);
|
||||||
|
|
||||||
u8 opacity;
|
u8 opacity;
|
||||||
if (time_passed < FADE_IN_TIME)
|
if (time_passed < NOTIFICATION_FADE_IN_TIME)
|
||||||
opacity = static_cast<u8>((time_passed / FADE_IN_TIME) * 255.0f);
|
opacity = static_cast<u8>((time_passed / NOTIFICATION_FADE_IN_TIME) * 255.0f);
|
||||||
else if (time_passed > (notif.duration - FADE_OUT_TIME))
|
else if (time_passed > (notif.duration - NOTIFICATION_FADE_OUT_TIME))
|
||||||
opacity = static_cast<u8>(std::min((notif.duration - time_passed) / FADE_OUT_TIME, 1.0f) * 255.0f);
|
opacity = static_cast<u8>(std::min((notif.duration - time_passed) / NOTIFICATION_FADE_OUT_TIME, 1.0f) * 255.0f);
|
||||||
else
|
else
|
||||||
opacity = 255;
|
opacity = 255;
|
||||||
|
|
||||||
@ -2431,7 +2536,8 @@ void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing)
|
|||||||
}
|
}
|
||||||
else if (actual_y != expected_y)
|
else if (actual_y != expected_y)
|
||||||
{
|
{
|
||||||
const float time_since_move = static_cast<float>(Common::Timer::ConvertValueToSeconds(current_time - notif.move_time));
|
const float time_since_move =
|
||||||
|
static_cast<float>(Common::Timer::ConvertValueToSeconds(current_time - notif.move_time));
|
||||||
if (time_since_move >= MOVE_DURATION)
|
if (time_since_move >= MOVE_DURATION)
|
||||||
{
|
{
|
||||||
notif.move_time = current_time;
|
notif.move_time = current_time;
|
||||||
|
@ -244,6 +244,8 @@ void RightAlignNavButtons(u32 num_items = 0, float item_width = LAYOUT_MENU_BUTT
|
|||||||
float item_height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
|
float item_height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
|
||||||
bool NavButton(const char* title, bool is_active, bool enabled = true, float width = -1.0f,
|
bool NavButton(const char* title, bool is_active, bool enabled = true, float width = -1.0f,
|
||||||
float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = g_large_font);
|
float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = g_large_font);
|
||||||
|
bool NavTab(const char* title, bool is_active, bool enabled, float width, float height, const ImVec4& background,
|
||||||
|
ImFont* font = g_large_font);
|
||||||
|
|
||||||
using FileSelectorCallback = std::function<void(const std::string& path)>;
|
using FileSelectorCallback = std::function<void(const std::string& path)>;
|
||||||
using FileSelectorFilters = std::vector<std::string>;
|
using FileSelectorFilters = std::vector<std::string>;
|
||||||
@ -286,7 +288,7 @@ void OpenBackgroundProgressDialog(const char* str_id, std::string message, s32 m
|
|||||||
void UpdateBackgroundProgressDialog(const char* str_id, std::string message, s32 min, s32 max, s32 value);
|
void UpdateBackgroundProgressDialog(const char* str_id, std::string message, s32 min, s32 max, s32 value);
|
||||||
void CloseBackgroundProgressDialog(const char* str_id);
|
void CloseBackgroundProgressDialog(const char* str_id);
|
||||||
|
|
||||||
void AddNotification(float duration, std::string title, std::string text, std::string image_path);
|
void AddNotification(std::string key, float duration, std::string title, std::string text, std::string image_path);
|
||||||
void ClearNotifications();
|
void ClearNotifications();
|
||||||
|
|
||||||
void ShowToast(std::string title, std::string message, float duration = 10.0f);
|
void ShowToast(std::string title, std::string message, float duration = 10.0f);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user