mirror of
https://github.com/BanjoRecomp/BanjoRecomp
synced 2026-06-07 11:27:31 -04:00
Fix intro cutscene timings (#56)
* Fix intro cutscene timings * Address review * Move code to individual functions * address review * Fix comments
This commit is contained in:
@@ -71,6 +71,7 @@ RECOMP_PATCH void graphicsCache_init(void){
|
||||
gTextureFilterPoint = 0;
|
||||
}
|
||||
|
||||
void handle_cutscene_timings(void);
|
||||
// @recomp Patched to check for graphics stack overflow after processing a frame.
|
||||
// Also patched to wait for a message when the displaylist is completed immediately after queueing it to solve vertex modification race conditions.
|
||||
RECOMP_PATCH void game_draw(s32 arg0){
|
||||
@@ -155,6 +156,9 @@ RECOMP_PATCH void game_draw(s32 arg0){
|
||||
}
|
||||
}
|
||||
|
||||
// @recomp Call the relevant function to fix cutscene timings, if there is one.
|
||||
handle_cutscene_timings();
|
||||
|
||||
// Allow interpolation for the next frame.
|
||||
set_all_interpolation_skipped(FALSE);
|
||||
}
|
||||
|
||||
@@ -93,11 +93,14 @@ void hotpatch_intro_opa_map_model(BKModelBin* model_bin) {
|
||||
gSPDisplayList(&dl[INTRO_OPA_DL_WALL_PATCH_INDEX], intro_wall_extension_dl);
|
||||
}
|
||||
|
||||
void reset_intro_cutscene_timings_state(void);
|
||||
|
||||
// @recomp Patched to act as a point to run code when a new map is loaded.
|
||||
// This includes:
|
||||
// * Resetting all extended marker data and skip interpolation for the next frame.
|
||||
// * Hotpatching the map model for the title cutscene to fix ultrawide effects.
|
||||
// * Resetting the spawned static note count.
|
||||
// * Resetting the variables used to keep track of correcting the intro cutscene timings
|
||||
RECOMP_PATCH void func_803329AC(void){
|
||||
s32 i;
|
||||
|
||||
@@ -127,4 +130,7 @@ RECOMP_PATCH void func_803329AC(void){
|
||||
|
||||
// @recomp Run note saving map load code.
|
||||
note_saving_on_map_load();
|
||||
|
||||
// @recomp Reset the intro cutscene timing corrections so the cutscene can be played again
|
||||
reset_intro_cutscene_timings_state();
|
||||
}
|
||||
|
||||
@@ -104,27 +104,34 @@ RECOMP_PATCH int demo_readInput(OSContPad* arg0, s32* arg1){
|
||||
return not_eof;
|
||||
}
|
||||
|
||||
int extraVis = 0;
|
||||
|
||||
// @recomp Patched to override the VI frame divisor when the demo frame divisor has been set.
|
||||
// Also overrides it if a cutscene needs timing compensation.
|
||||
RECOMP_PATCH s32 viMgr_func_8024BFA0() {
|
||||
if (demo_frame_divisor != -1) {
|
||||
return demo_frame_divisor;
|
||||
}
|
||||
return D_802808DC;
|
||||
return D_802808DC + extraVis;
|
||||
}
|
||||
|
||||
// @recomp Patched to clear the demo frame divisor after viMgr_func_8024BFD8.
|
||||
// @recomp Patched to clear lag overrides after viMgr_func_8024BFD8.
|
||||
RECOMP_PATCH void viMgr_func_8024C1B4(void){
|
||||
viMgr_func_8024BFD8(0);
|
||||
// @recomp Clear the demo frame divisor.
|
||||
demo_frame_divisor = -1;
|
||||
dummy_func_8025AFB8();
|
||||
// @recomp Clear the lag override for cutscenes.
|
||||
extraVis = 0;
|
||||
}
|
||||
|
||||
// @recomp Patched to clear the demo frame divisor after viMgr_func_8024BFD8.
|
||||
// @recomp Patched to clear lag overrides after viMgr_func_8024BFD8.
|
||||
RECOMP_PATCH void viMgr_func_8024C1DC(void){
|
||||
viMgr_func_8024BFD8(1);
|
||||
// @recomp Clear the demo frame divisor.
|
||||
demo_frame_divisor = -1;
|
||||
// @recomp Clear the lag override for cutscenes.
|
||||
extraVis = 0;
|
||||
}
|
||||
|
||||
// @recomp Patched to use a fixed time delta of 30 FPS when decrementing the hourglass timer during Bottles' Bonus.
|
||||
@@ -142,3 +149,72 @@ RECOMP_PATCH void func_80345EB0(enum item_e item){
|
||||
func_802FACA4(item);
|
||||
}
|
||||
}
|
||||
|
||||
// The intro cutscene stutters on console, but it does not stutter in recomp.
|
||||
// The cutscene is timed with the stutters in mind so this causes desyncs with the music and sound effects.
|
||||
// We have manually analyzed the cutscene and taken note of the exact frames during which it stutters,
|
||||
// and we lag the game to 15 FPS internally (this cutscene targets 20 FPS) for a few frames when it would
|
||||
// have stuttered on console in order to keep the cutscene in sync.
|
||||
|
||||
// What frames of the cutscene to lag on, and for how many frames.
|
||||
int introStuttersStartFrames[] = { 269, 521, 583, 663, 769, 959, 1155, 1182, 1214 };
|
||||
int introStutterDurations[] = { 4, 4, 4, 4, 4, 4, 4, 4, 4 };
|
||||
|
||||
// These are reset on map load, so that the cutscene can be replayed (such as when saving and exiting)
|
||||
// See func_803329AC in load_patches.c
|
||||
int introCutsceneCounter = 0;
|
||||
int introCutsceneNextStutter = 0;
|
||||
int introCutsceneLagIndex = 0;
|
||||
|
||||
bool should_lag_intro_cutscene(void) {
|
||||
// No stutters left to compensate for. Exit the function early.
|
||||
if (introCutsceneNextStutter == -1) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// First frame of the cutscene. Set the first stutter frame.
|
||||
if (introCutsceneNextStutter < introStuttersStartFrames[0]) {
|
||||
introCutsceneNextStutter = introStuttersStartFrames[0];
|
||||
//recomp_printf("Start intro cutscene with timing corrections. First stutter frame: %d\n", introCutsceneNextStutter);
|
||||
}
|
||||
|
||||
if (introCutsceneCounter >= (introCutsceneNextStutter) && introCutsceneCounter < (introCutsceneNextStutter + introStutterDurations[introCutsceneLagIndex])) {
|
||||
// A stutter would have occured on console now. Lag the game for a given amount of frames.
|
||||
//recomp_printf("LAGGING. Stutter number %d. Frame number %d\n", introCutsceneLagIndex, introCutsceneCounter);
|
||||
return TRUE;
|
||||
} else if (introCutsceneCounter > (introCutsceneNextStutter)) {
|
||||
introCutsceneLagIndex++;
|
||||
if (introCutsceneLagIndex >= (int)sizeof(introStuttersStartFrames) / (int)sizeof(introStuttersStartFrames[0])) {
|
||||
// That was the last stutter. We're done.
|
||||
introCutsceneNextStutter = -1;
|
||||
//recomp_printf("End intro cutscene. %d\n", introCutsceneNextStutter);
|
||||
} else {
|
||||
// Set the next stutter frame.
|
||||
introCutsceneNextStutter = introStuttersStartFrames[introCutsceneLagIndex];
|
||||
//recomp_printf("Next stutter: %d\n", introCutsceneNextStutter);
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
// Reset the custom cutscene frame counter and the stutter frame index used to
|
||||
// correct the timings of the intro cutscene.
|
||||
void reset_intro_cutscene_timings_state(void) {
|
||||
introCutsceneCounter = 0;
|
||||
introCutsceneNextStutter = 0;
|
||||
introCutsceneLagIndex = 0;
|
||||
}
|
||||
|
||||
// Check the current map to see if it's a cutscene map that requires timing fixes,
|
||||
// and run the relevant function if so.
|
||||
void handle_cutscene_timings(void) {
|
||||
switch (map_get()) {
|
||||
case MAP_1E_CS_START_NINTENDO:
|
||||
if (should_lag_intro_cutscene()) {
|
||||
extraVis = 1;
|
||||
}
|
||||
introCutsceneCounter++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user