Fix the reset button doing nothing or looping the start screen when pressed repeatedly (#719)

* Make the reset button reliable from any screen

Pressing reset repeatedly near app start could do nothing, fade the music
with no reset, or bounce the press-start screen back to itself. Three
defects in the old handler:

- It ran from the ImGui widget, racing the menu state machine; a press
  landing mid-fade was re-advanced by the in-flight transition (the
  press-start loop).
- It always wrote gGamestateNext = MAIN_MENU_FROM_QUIT; once the game was
  already in that state a repeat write is swallowed by the != guard in
  main.c, so nothing happened.
- CM_ResetAudio ran unconditionally, so the music faded even when the
  reset was swallowed.

The widget now only sets an atomic request flag; ApplyPendingReset (top of
push_frame, on the game loop) performs the reset. It alternates between
the two identical FROM_QUIT gamestates so every press trips the gamestate
switch, and re-enters the menus through the intro's own transition
protocol (FADE_MODE_LOGO -> setup_menus rebuild + fresh fade-in), which
replaces any in-flight transition. Audio only fades when a reset actually
executes. The dead gSkipIntro switch in the old handler (an unconditional
override below it always won) is gone; behavior is unchanged: reset lands
on the logo intro, or the start menu in debug mode.

Play-verified: hammering reset during the splash screens restarts the
logo every press; reset mid-fade after press-start no longer loops back;
resets from the main menu and mid-race work as before.

* Honor gSkipIntro when picking the post-reset screen

Review feedback on #719: restore the gSkipIntro switch from the old
handler instead of hard-coding the logo intro, keeping the debug-mode
override on top (no else), matching the boot-time logic in main().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: MegaMech <MegaMech@users.noreply.github.com>
This commit is contained in:
quarrel07
2026-07-23 11:21:36 -07:00
committed by GitHub
parent 4c24ae2bc2
commit 78a012ce06
3 changed files with 72 additions and 40 deletions
+67
View File
@@ -6,6 +6,7 @@
#include <fast/Fast3dWindow.h>
#include <memory>
#include <atomic>
#include "engine/World.h"
#include "engine/AllTracks.h"
@@ -911,7 +912,73 @@ void CM_ResetAudio(void) {
}
}
static std::atomic<bool> sResetRequested{ false };
void CM_RequestReset(void) {
sResetRequested.store(true);
}
// The reset widget only requests; the reset is applied here at the top of the
// game frame. Applying it from the widget raced the menu state machine: a
// press landing mid-fade was re-advanced by the in-flight transition, and a
// repeat press rewrote the gamestate it had already set, which the != guard
// in main.c swallows while the audio fade still runs (silent no-op).
static void ApplyPendingReset() {
if (!sResetRequested.exchange(false)) {
return;
}
// The FROM_QUIT gamestates run identical inits; alternating keeps
// gGamestateNext != gGamestate true so every press re-enters the menus.
gGamestateNext = (gGamestate == MAIN_MENU_FROM_QUIT) ? START_MENU_FROM_QUIT : MAIN_MENU_FROM_QUIT;
gIsGamePaused = 0;
// Reset credits
D_800DC5E4 = 0;
gTourComplete = false;
SetMarioRaceway();
memset(&gGameModeMenuColumn, 0, sizeof(s8) * NUM_ROWS_GAME_MODE_MENU);
memset(&gGameModeSubMenuColumn, 0, sizeof(s8) * NUM_COLUMN_GAME_MODE_SUB_MENU * NUM_ROWS_GAME_MODE_SUB_MENU);
CM_ResetAudio();
// Close the editor.
if (gEditor.IsEnabled()) {
gEditor.Disable();
}
// Set the debug menu track browsing index back to zero
TrackBrowser::Instance->Reset();
// Land on the same screen the gSkipIntro setting picks at boot.
switch (CVarGetInteger("gSkipIntro", 0)) {
case 0:
gMenuSelection = HARBOUR_MASTERS_MENU;
break;
case 1:
gMenuSelection = LOGO_INTRO_MENU;
break;
case 2:
gMenuSelection = START_MENU;
break;
case 3:
gMenuSelection = MAIN_MENU;
break;
}
// Debug mode override gSkipIntro
if (CVarGetInteger("gEnableDebugMode", 0) == true) {
gMenuSelection = START_MENU;
}
// Re-enter through the intro's own transition protocol (see HM_TickIntro):
// FADE_MODE_LOGO makes setup_menus rebuild the menu items and start a
// fresh fade-in, replacing any in-flight transition that would otherwise
// advance the stale screen right after the reset.
gMenuFadeType = 0;
gFadeModeSelection = FADE_MODE_LOGO;
}
void push_frame() {
ApplyPendingReset();
GameEngine::StartAudioFrame();
GameEngine::Instance->StartFrame();
thread5_iteration();
+1
View File
@@ -35,6 +35,7 @@ extern Registry<ActorInfo, const SpawnParams&> gActorRegistry;
extern Registry<ItemInfo> gItemRegistry;
extern DataRegistry<RandomItemTable> gItemTableRegistry;
World* GetWorld(void); // Retrieve the world instance
void CM_RequestReset(void); // Queue a game reset; applied at the top of the next game frame
#endif
// NOLINTBEGIN(readability-identifier-naming)
+4 -40
View File
@@ -62,46 +62,10 @@ class Menu : public GuiWindow {
"Searches all menus for the given text, including tooltips.")) } } }
};
virtual void ProcessReset() {
gGamestateNext = MAIN_MENU_FROM_QUIT;
gIsGamePaused = 0;
// Reset credits
D_800DC5E4 = 0;
gTourComplete = false;
SetMarioRaceway();
memset(&gGameModeMenuColumn, 0, sizeof(s8) * NUM_ROWS_GAME_MODE_MENU);
memset(&gGameModeSubMenuColumn, 0, sizeof(s8) * NUM_COLUMN_GAME_MODE_SUB_MENU * NUM_ROWS_GAME_MODE_SUB_MENU);
CM_ResetAudio();
switch(CVarGetInteger("gSkipIntro", 0)) {
case 0:
gMenuSelection = HARBOUR_MASTERS_MENU;
break;
case 1:
gMenuSelection = LOGO_INTRO_MENU;
break;
case 2:
gMenuSelection = START_MENU;
break;
case 3:
gMenuSelection = MAIN_MENU;
break;
}
// Close the editor.
if (gEditor.IsEnabled()) {
gEditor.Disable();
}
// Set the debug menu track browsing index back to zero
TrackBrowser::Instance->Reset();
// Debug mode override gSkipIntro
if (CVarGetInteger("gEnableDebugMode", 0) == true) {
gMenuSelection = START_MENU;
} else {
gMenuSelection = LOGO_INTRO_MENU;
}
// Only request the reset: it is applied at the top of the next game
// frame (ApplyPendingReset in Game.cpp), where it cannot race the menu
// state machine mid-fade or be swallowed as a repeated gamestate write.
CM_RequestReset();
}
private: