Achievements: Harden RetroAchievements Hardcore Compliance

Fixes #13273
 - Totally block save state loading, frame advance, and disable cheats in hardcore
 - Disable load state UI buttons when hardcore mode active (leave message telling them if they want to load state if the user somehow gets past this)
 - Add consistent OSD messaging for all hardcore restrictions

Signed-off-by: SternXD <stern@sidestore.io>
This commit is contained in:
SternXD 2025-09-20 22:30:02 -04:00 committed by Ty
parent 251140fd52
commit 49111a6fbe
3 changed files with 25 additions and 28 deletions

View File

@ -568,7 +568,7 @@ void MainWindow::recreate()
if (was_display_created)
{
g_emu_thread->setSurfaceless(false);
g_main_window->updateEmulationActions(false, s_vm_valid, Achievements::IsHardcoreModeActive());
g_main_window->updateEmulationActions(false, s_vm_valid, false);
g_main_window->onFullscreenUIStateChange(g_emu_thread->isRunningFullscreenUI());
}
}
@ -852,6 +852,10 @@ void MainWindow::onAchievementsHardcoreModeChanged(bool enabled)
{
// disable debugger while hardcore mode is active
m_ui.actionDebugger->setDisabled(enabled);
// refresh emulation actions to show/hide load state buttons based on hardcore mode
updateEmulationActions(s_vm_valid, s_vm_valid, false);
if (enabled)
{
// If PauseOnEntry is enabled, we prompt the user to disable Hardcore Mode
@ -947,7 +951,7 @@ void MainWindow::updateEmulationActions(bool starting, bool running, bool stoppi
m_ui.actionPause->setEnabled(running);
m_ui.actionScreenshot->setEnabled(running);
m_ui.menuChangeDisc->setEnabled(running);
m_ui.menuLoadState->setEnabled(running);
m_ui.menuLoadState->setEnabled(running && !Achievements::IsHardcoreModeActive());
m_ui.menuSaveState->setEnabled(running);
m_ui.actionSaveGSDump->setEnabled(running);
@ -956,7 +960,7 @@ void MainWindow::updateEmulationActions(bool starting, bool running, bool stoppi
m_ui.actionToolbarPause->setEnabled(running);
m_ui.actionToolbarScreenshot->setEnabled(running);
m_ui.actionToolbarChangeDisc->setEnabled(running);
m_ui.actionToolbarLoadState->setEnabled(running);
m_ui.actionToolbarLoadState->setEnabled(running && !Achievements::IsHardcoreModeActive());
m_ui.actionToolbarSaveState->setEnabled(running);
m_ui.actionViewGameProperties->setEnabled(running);

View File

@ -5753,7 +5753,8 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
case PauseSubMenu::None:
{
// NOTE: Menu close must come first, because otherwise VM destruction options will race.
const bool can_load_or_save_state = s_current_disc_crc != 0;
const bool can_load_state = s_current_disc_crc != 0 && !Achievements::IsHardcoreModeActive();
const bool can_save_state = s_current_disc_crc != 0;
if (just_focused)
ImGui::SetFocusID(ImGui::GetID(FSUI_ICONSTR(ICON_FA_PLAY, "Resume Game")), ImGui::GetCurrentWindow());
@ -5767,19 +5768,19 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
DoToggleFrameLimit();
}
if (ActiveButton(FSUI_ICONSTR(ICON_FA_ARROW_ROTATE_LEFT, "Load State"), false, can_load_or_save_state))
if (ActiveButton(FSUI_ICONSTR(ICON_FA_ARROW_ROTATE_LEFT, "Load State"), false, can_load_state))
{
if (OpenSaveStateSelector(true))
s_current_main_window = MainWindowType::None;
}
if (ActiveButton(FSUI_ICONSTR(ICON_FA_DOWNLOAD, "Save State"), false, can_load_or_save_state))
if (ActiveButton(FSUI_ICONSTR(ICON_FA_DOWNLOAD, "Save State"), false, can_save_state))
{
if (OpenSaveStateSelector(false))
s_current_main_window = MainWindowType::None;
}
if (ActiveButton(FSUI_ICONSTR(ICON_FA_WRENCH, "Game Properties"), false, can_load_or_save_state))
if (ActiveButton(FSUI_ICONSTR(ICON_FA_WRENCH, "Game Properties"), false, can_save_state))
{
SwitchToGameSettings();
}
@ -5819,7 +5820,7 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
if (ActiveButton(FSUI_ICONSTR(ICON_FA_POWER_OFF, "Close Game"), false))
{
// skip submenu when we can't save anyway
if (!can_load_or_save_state)
if (!can_save_state)
RequestShutdown(false);
else
OpenPauseSubMenu(PauseSubMenu::Exit);
@ -6144,7 +6145,7 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
if (ActiveButton(
is_loading ? FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Load State") : FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Save State"),
false, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY))
false, is_loading ? !Achievements::IsHardcoreModeActive() : true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY))
{
if (is_loading)
DoLoadState(std::move(entry.path));

View File

@ -1970,11 +1970,9 @@ bool VMManager::LoadState(const char* filename)
{
if (Achievements::IsHardcoreModeActive())
{
Achievements::ConfirmHardcoreModeDisableAsync(TRANSLATE("VMManager", "Loading state"),
[filename = std::string(filename)](bool approved) {
if (approved)
LoadState(filename.c_str());
});
Host::AddIconOSDMessage("LoadStateHardcoreBlocked", ICON_FA_TRIANGLE_EXCLAMATION,
TRANSLATE_SV("VMManager", "Cannot load save state while RetroAchievements Hardcore Mode is active."),
Host::OSD_WARNING_DURATION);
return false;
}
@ -2007,11 +2005,9 @@ bool VMManager::LoadStateFromSlot(s32 slot, bool backup)
if (Achievements::IsHardcoreModeActive())
{
Achievements::ConfirmHardcoreModeDisableAsync(TRANSLATE("VMManager", "Loading state"),
[slot](bool approved) {
if (approved)
LoadStateFromSlot(slot);
});
Host::AddIconOSDMessage("LoadStateHardcoreBlocked", ICON_FA_TRIANGLE_EXCLAMATION,
fmt::format(TRANSLATE_FS("VMManager", "Cannot load save {} from slot {} while RetroAchievements Hardcore Mode is active."), backup ? TRANSLATE("VMManager", "backup state") : TRANSLATE("VMManager", "state"), slot),
Host::OSD_WARNING_DURATION);
return false;
}
@ -2223,12 +2219,9 @@ void VMManager::FrameAdvance(u32 num_frames /*= 1*/)
if (Achievements::IsHardcoreModeActive())
{
Achievements::ConfirmHardcoreModeDisableAsync(TRANSLATE("VMManager", "Frame advancing"),
[num_frames](bool approved) {
if (approved)
FrameAdvance(num_frames);
});
Host::AddIconOSDMessage("FrameAdvanceHardcoreBlocked", ICON_FA_TRIANGLE_EXCLAMATION,
TRANSLATE_SV("VMManager", "Cannot frame advance while RetroAchievements Hardcore Mode is active."),
Host::OSD_WARNING_DURATION);
return;
}
@ -3067,7 +3060,6 @@ void VMManager::EnforceAchievementsChallengeModeSettings()
{
if (!Achievements::IsHardcoreModeActive())
{
Host::RemoveKeyedOSDMessage("ChallengeDisableCheats");
return;
}
@ -3084,8 +3076,8 @@ void VMManager::EnforceAchievementsChallengeModeSettings()
// Can't use cheats.
if (EmuConfig.EnableCheats)
{
Host::AddKeyedOSDMessage("ChallengeDisableCheats",
TRANSLATE_STR("VMManager", "Cheats have been disabled due to achievements hardcore mode."),
Host::AddIconOSDMessage("ChallengeDisableCheats", ICON_FA_TRIANGLE_EXCLAMATION,
TRANSLATE_SV("VMManager", "Cheats have been disabled due to RetroAchievements Hardcore Mode."),
Host::OSD_WARNING_DURATION);
EmuConfig.EnableCheats = false;
}