coop-deluxe/sm64coopdx

Exiting to Castle Advances Gameplay by a Single Frame Before Freezing Again in Singleplayer

Closed this issue · 2 comments

Normally, all gameplay is paused when pausing and exiting to castle in the original game.
https://github.com/user-attachments/assets/25cc0146-6422-4b0f-9a71-43bd6c9de24b

Although in CoopDX, gameplay moves just a single frame forward before actually pausing gameplay properly.
https://github.com/user-attachments/assets/06a41f8b-31a1-457a-ba41-032f4c71430b

To better visualize this, pay attention to the red coin in the CoopDX clip.

Before:
image

After:
image

And to show that this affects the player as well, watch this clip:
https://github.com/user-attachments/assets/8c21dac8-a17c-42ef-9bbf-4f9eef87a48c

Weird

Alright, found the reason why extra frame is processed on the pause screen.

First, this is executed when player presses START and pausing is allowed:

} else if (sCurrPlayMode == PLAY_MODE_NORMAL && pressed_pause()) {
lower_background_noise(1);
cancel_rumble();
gCameraMovementFlags |= CAM_MOVE_PAUSE_SCREEN;
set_play_mode(PLAY_MODE_PAUSED);
}

Then, the game enters 2nd game loop iteration.

Now, network_check_singleplayer_pause() still returns FALSE, because (gMenuMode == (-1)):

bool network_check_singleplayer_pause(void) {
return gMenuMode != -1 && !gServerSettings.pauseAnywhere && network_player_connected_count() == 1 && mods_get_all_pausable() && !gDjuiInPlayerMenu;
}

And when this code in update_level() is reached, the game executes:
(1) changeLevel = play_mode_normal();
(2) sCurrPlayMode has not changed from PLAY_MODE_PAUSED
(3) changeLevel = play_mode_paused();
(4) { set_menu_mode(RENDER_PAUSE_SCREEN); }

case PLAY_MODE_PAUSED:
if (!network_check_singleplayer_pause()) {
changeLevel = play_mode_normal();
}
if (sCurrPlayMode == PLAY_MODE_PAUSED) {
changeLevel = play_mode_paused();
}
break;

(normally this is so that the gameplay continues, while the pause screen is rendered in multiplayer, or when one of the loaded mods has property pausable: false)


To catch the moment where player wants to (and is allowed to) pause in singleplayer, but play_mode_paused() was not called yet, perhaps this would do the trick:

bool network_check_singleplayer_pause(void) {
    extern s16 gMenuMode;
    extern s16 gCameraMovementFlags;
    return ((gMenuMode != -1) || (gCameraMovementFlags & CAM_MOVE_PAUSE_SCREEN)) &&
    !gDjuiInPlayerMenu && network_player_connected_count() == 1 && mods_get_all_pausable();
}