diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 5075ab50d..4546aa1fc 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2682,6 +2682,9 @@ void CL_Reset(void) if (demo.recording) G_CheckDemoStatus(); + // don't carry menus into the title screen (or wherever we're going) + M_ClearMenus(true); + // reset client/server code DEBFILE(va("\n-=-=-=-=-=-=-= Client reset =-=-=-=-=-=-=-\n\n")); diff --git a/src/d_main.cpp b/src/d_main.cpp index 828f2023b..56c8cb5b0 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -132,6 +132,8 @@ boolean devparm = false; // started game with -devparm boolean singletics = false; // timedemo boolean lastdraw = false; +static INT32 lastwipetic = 0; + INT32 postimgparam[MAXSPLITSCREENPLAYERS]; // These variables are in effect @@ -187,6 +189,8 @@ UINT8 ctrldown = 0; // 0x1 left, 0x2 right UINT8 altdown = 0; // 0x1 left, 0x2 right boolean capslock = 0; // gee i wonder what this does. +static boolean recursioncheck = false; + // // D_ProcessEvents // Send all the events of the given timestamp down the responder chain @@ -195,7 +199,12 @@ void D_ProcessEvents(void) { event_t *ev; - boolean eaten; + boolean eaten = false; + + if (recursioncheck == true) + I_Error("D_ProcessEvents recursion detected"); + + recursioncheck = true; // i have to reset this somewhere or else your camera just glides away! for (size_t i = 0; i < 4; i++) @@ -223,15 +232,16 @@ void D_ProcessEvents(void) } // Menu input -#ifdef HAVE_THREADS - I_lock_mutex(&m_menu_mutex); -#endif + if (WipeInAction < 2) { - eaten = M_Responder(ev); - } #ifdef HAVE_THREADS - I_unlock_mutex(m_menu_mutex); + I_lock_mutex(&m_menu_mutex); #endif + eaten = M_Responder(ev); +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif + } if (eaten) continue; // menu ate the event @@ -260,6 +270,8 @@ void D_ProcessEvents(void) G_Responder(ev); } + + recursioncheck = false; } static void D_RenderLevel(void) @@ -388,9 +400,8 @@ gamestate_t wipegamestate = GS_LEVEL; INT16 wipetypepre = -1; INT16 wipetypepost = -1; -static bool D_Display(void) +static void D_Display(void) { - bool ranwipe = false; boolean forcerefresh = false; static boolean wipe = false; INT32 wipedefindex = 0; @@ -401,7 +412,7 @@ static bool D_Display(void) if (!dedicated) { if (nodrawers) - return false; // for comparative timing/profiling + return; // for comparative timing/profiling // Lactozilla: Switching renderers works by checking // if the game has to do it right when the frame @@ -460,21 +471,23 @@ static bool D_Display(void) if (wipetypepre < 0 || !F_WipeExists(wipetypepre)) wipetypepre = wipedefs[wipedefindex]; - if (rendermode != render_none) + // Fade to black first + if ((wipegamestate == FORCEWIPE || + !(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) // fades to black on its own timing, always + && wipetypepre != UINT8_MAX) { - // Fade to black first - if ((wipegamestate == FORCEWIPE || - !(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) // fades to black on its own timing, always - && wipetypepre != UINT8_MAX) + if (rendermode != render_none) { F_WipeStartScreen(); F_WipeColorFill(31); F_WipeEndScreen(); - F_RunWipe(wipetypepre, gamestate != GS_TIMEATTACK && gamestate != GS_TITLESCREEN); - ranwipe = true; } + F_RunWipe(wipetypepre, gamestate != GS_TIMEATTACK && gamestate != GS_TITLESCREEN); + } - if (gamestate != GS_LEVEL && rendermode != render_none) + if (rendermode != render_none) + { + if (gamestate != GS_LEVEL) { V_SetPaletteLump("PLAYPAL"); // Reset the palette R_ReInitColormaps(0, NULL, 0, false); @@ -482,20 +495,14 @@ static bool D_Display(void) F_WipeStartScreen(); } - else //dedicated servers - { - F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK && gamestate != GS_TITLESCREEN); - ranwipe = true; + else wipegamestate = gamestate; - } - - wipetypepre = -1; } - else - wipetypepre = -1; + + wipetypepre = -1; if (dedicated) //bail out after wipe logic - return false; + goto dedipostwipe; // WAIT! don't forget about post wipes! // Catch runaway clipping rectangles. V_ClearClipRect(); @@ -587,6 +594,7 @@ static bool D_Display(void) } case GS_DEDICATEDSERVER: case GS_NULL: + case FORCEWIPE: break; } @@ -658,6 +666,7 @@ static bool D_Display(void) // // wipe update // +dedipostwipe: if (wipe && wipetypepost != INT16_MAX) { // note: moved up here because NetUpdate does input changes @@ -668,12 +677,9 @@ static bool D_Display(void) wipetypepost = wipedefs[wipedefindex]; if (rendermode != render_none) - { F_WipeEndScreen(); - F_RunWipe(wipetypepost, gamestate != GS_TIMEATTACK && gamestate != GS_TITLESCREEN); - ranwipe = true; - } + F_RunWipe(wipetypepost, gamestate != GS_TIMEATTACK); // reset counters so timedemo doesn't count the wipe duration if (demo.timing) @@ -681,11 +687,12 @@ static bool D_Display(void) framecount = 0; demostarttime = I_GetTime(); } - - wipetypepost = -1; } - else - wipetypepost = -1; + + wipetypepost = -1; + + if (dedicated) + return; // NOW we can bail NetUpdate(); // send out any new accumulation @@ -730,8 +737,185 @@ static bool D_Display(void) I_FinishUpdate(); // page flip or blit buffer ps_swaptime = I_GetPreciseTime() - ps_swaptime; } +} - return ranwipe; +static void D_WipeTick(boolean menu) +{ + I_OsPolling(); + D_ProcessEvents(); + + // Update the network so we don't cause timeouts + NetKeepAlive(); + + HU_Ticker(); + if (menu) + { +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif + M_Ticker(); +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif + } + CON_Ticker(); +} + +static void D_WipeDraw(boolean menu) +{ + if (rendermode == render_none) + return; + + I_UpdateNoBlit(); + + HU_Erase(); + HU_Drawer(); + if (menu) + { +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif + M_Drawer(); +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif + } + CON_Drawer(); + + I_FinishUpdate(); // page flip or blit buffer +} + +static double D_EndFrame(precise_t enterprecise, int *frameskip) +{ + // Fully completed frame made. + precise_t finishprecise = I_GetPreciseTime(); + + // Use the time before sleep for frameskip calculations: + // post-sleep time is literally being intentionally wasted + double deltasecs = (double)((INT64)(finishprecise - enterprecise)) / I_GetPrecisePrecision(); + double deltatics = deltasecs * NEWTICRATE; + + // If time spent this game loop exceeds a single tic, + // it's probably because of rendering. + // + // Skip rendering the next frame, up to a limit of 3 + // frames before a frame is rendered no matter what. + if (frameskip) + { + if (*frameskip < 3 && deltatics > 1.0) + *frameskip += 1; + else + *frameskip = 0; + } + + if (!singletics) + { + precise_t elapsed = finishprecise - enterprecise; + + // capbudget is the minimum precise_t duration of a single loop iteration + precise_t capbudget = round((1.0 / R_GetFramerateCap()) * I_GetPrecisePrecision()); + + // in the case of "match refresh rate" + vsync, don't sleep at all + const boolean vsync_with_match_refresh = cv_vidwait.value && cv_fpscap.value == 0; + + if (elapsed > 0 && capbudget > elapsed && !vsync_with_match_refresh) + { + I_SleepDuration(capbudget - (finishprecise - enterprecise)); + } + } + + // Capture the time once more to get the real delta time. + finishprecise = I_GetPreciseTime(); + deltasecs = (double)((INT64)(finishprecise - enterprecise)) / I_GetPrecisePrecision(); + return deltasecs * NEWTICRATE; +} + +static INT32 endtimes[] = { + UINT8_MAX, // WIPELOOP_RUNWIPE + PRELEVELTIME*NEWTICRATERATIO, // WIPELOOP_TITLECARD + 3*TICRATE/2, // WIPELOOP_ENCORE + NEWTICRATE, // WIPELOOP_TITLEBLACK +}; + +static bool ranwipe = false; + +// one single function for all the extra main loops in this god-forsaken codebase +// should make it easier to fold these back into D_SRB2Loop at some point... +void D_WipeLoop(wipelooptype_t type, UINT8 wipetype, boolean drawMenu) +{ + tic_t nowtime, endtime; + UINT8 wipeframe = 0; + fademask_t *fmask; + double delta = 0.0; + + nowtime = lastwipetic = I_GetTime(); + endtime = nowtime + endtimes[type]; + + if (gamestate != GS_TITLESCREEN && gamestate != GS_TIMEATTACK) + drawMenu = true; + + WipeInAction = 1 + !drawMenu; + + // lastwipetic should either be 0 or the tic we last wiped + // on for fade-to-black + while (nowtime < endtime) + { + precise_t enterprecise = I_GetPreciseTime(); + I_UpdateTime(cv_timescale.value); + + nowtime = I_GetTime(); + + // wait loop + if (nowtime - lastwipetic) + { + renderisnewtic = true; + wipeframe++; + D_WipeTick(drawMenu); + if (type == WIPELOOP_TITLECARD) + ST_runTitleCard(); + } + else + renderisnewtic = false; + + // draw loop + rendertimefrac = g_time.timefrac; + renderdeltatics = FLOAT_TO_FIXED(delta); + +#ifndef NOWIPE + if (type == WIPELOOP_RUNWIPE) + { + // get fademask first so we can tell if it exists or not + fmask = F_GetFadeMask(wipetype, wipeframe); + if (!fmask) + break; + +#ifdef HWRENDER + if (rendermode == render_opengl) + HWR_DoWipe(wipetype, wipeframe); // send in the wipe type and wipeframe because we need to cache the graphic + else +#endif + if (rendermode != render_none) //this allows F_RunWipe to be called in dedicated servers + F_DoWipe(fmask); + } +#endif + + if (rendermode != render_none && type == WIPELOOP_TITLECARD) + ST_preLevelTitleCardDrawer(); + + D_WipeDraw(drawMenu); + + // Only take screenshots after drawing. + if (moviemode) + M_SaveFrame(); + if (takescreenshot) + M_DoScreenShot(); + + delta = D_EndFrame(enterprecise, NULL); + lastwipetic = nowtime; + } + + ranwipe = true; + WipeInAction = 0; } // ========================================================================= @@ -744,7 +928,6 @@ void D_SRB2Loop(void) { tic_t entertic = 0, oldentertics = 0, realtics = 0, rendertimeout = INFTICS; double deltatics = 0.0; - double deltasecs = 0.0; boolean interp = false; boolean doDisplay = false; @@ -797,22 +980,11 @@ void D_SRB2Loop(void) for (;;) { - // capbudget is the minimum precise_t duration of a single loop iteration - precise_t capbudget; precise_t enterprecise = I_GetPreciseTime(); - precise_t finishprecise = enterprecise; g_dc = {}; Z_Frame_Reset(); - { - // Casting the return value of a function is bad practice (apparently) - double budget = round((1.0 / R_GetFramerateCap()) * I_GetPrecisePrecision()); - capbudget = (precise_t) budget; - } - - bool ranwipe = false; - I_UpdateTime(cv_timescale.value); if (lastwipetic) @@ -926,7 +1098,7 @@ void D_SRB2Loop(void) { if (!frameskip) { - ranwipe = D_Display(); + D_Display(); } else if (!dedicated && frameskip) { @@ -965,47 +1137,16 @@ void D_SRB2Loop(void) } #endif - // Fully completed frame made. - finishprecise = I_GetPreciseTime(); + deltatics = D_EndFrame(enterprecise, &frameskip); - // Use the time before sleep for frameskip calculations: - // post-sleep time is literally being intentionally wasted - deltasecs = (double)((INT64)(finishprecise - enterprecise)) / I_GetPrecisePrecision(); - deltatics = deltasecs * NEWTICRATE; - - // If time spent this game loop exceeds a single tic, - // it's probably because of rendering. - // - // Skip rendering the next frame, up to a limit of 3 - // frames before a frame is rendered no matter what. - // // Wipes run an inner loop and artificially increase // the measured time. - if (!ranwipe && frameskip < 3 && deltatics > 1.0) - { - frameskip++; - } - else + if (ranwipe) { + deltatics = 35.0 / R_GetFramerateCap(); frameskip = 0; + ranwipe = false; } - - if (!singletics) - { - INT64 elapsed = (INT64)(finishprecise - enterprecise); - - // in the case of "match refresh rate" + vsync, don't sleep at all - const boolean vsync_with_match_refresh = cv_vidwait.value && cv_fpscap.value == 0; - - if (elapsed > 0 && (INT64)capbudget > elapsed && !vsync_with_match_refresh) - { - I_SleepDuration(capbudget - (finishprecise - enterprecise)); - } - } - // Capture the time once more to get the real delta time. - finishprecise = I_GetPreciseTime(); - deltasecs = (double)((INT64)(finishprecise - enterprecise)) / I_GetPrecisePrecision(); - deltatics = deltasecs * NEWTICRATE; } } diff --git a/src/d_main.h b/src/d_main.h index 5210c76be..a9749408a 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -61,6 +61,16 @@ extern char srb2path[256]; //Alam: SRB2's Home // the infinite loop of D_SRB2Loop() called from win_main for windows version void D_SRB2Loop(void) FUNCNORETURN; +typedef enum +{ + WIPELOOP_RUNWIPE, + WIPELOOP_TITLECARD, + WIPELOOP_ENCORE, + WIPELOOP_TITLEBLACK, +} wipelooptype_t; + +void D_WipeLoop(wipelooptype_t type, UINT8 wipetype, boolean drawMenu); + // // D_SRB2Main() // Not a globally visible function, just included for source reference, diff --git a/src/f_finale.c b/src/f_finale.c index 6f2e0f6f4..925b57b89 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -431,34 +431,7 @@ void F_IntroTicker(void) } // Stay on black for a bit. =) - { - tic_t nowtime, quittime, lasttime; - nowtime = lasttime = I_GetTime(); - quittime = nowtime + NEWTICRATE; // Shortened the quit time, used to be 2 seconds - while (quittime > nowtime) - { - while (!((nowtime = I_GetTime()) - lasttime)) - { - I_Sleep(cv_sleep.value); - I_UpdateTime(cv_timescale.value); - } - lasttime = nowtime; - - I_OsPolling(); - I_UpdateNoBlit(); -#ifdef HAVE_THREADS - I_lock_mutex(&m_menu_mutex); -#endif - M_Drawer(); // menu is drawn even on top of wipes -#ifdef HAVE_THREADS - I_unlock_mutex(m_menu_mutex); -#endif - I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001 - - if (moviemode) // make sure we save frames for the white hold too - M_SaveFrame(); - } - } + D_WipeLoop(WIPELOOP_TITLEBLACK, 0, false); D_StartTitle(); return; diff --git a/src/f_finale.h b/src/f_finale.h index 4c8da0b23..471b74568 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -26,9 +26,6 @@ extern "C" { // // FINALE // -// HACK for menu fading while titlemapinaction; skips the level check -#define FORCEWIPE -2 - // Called by main loop. boolean F_IntroResponder(event_t *ev); @@ -142,15 +139,15 @@ void F_MenuPresTicker(boolean run); // WIPE // -extern boolean WipeInAction; +extern UINT8 WipeInAction; extern boolean WipeStageTitle; -extern INT32 lastwipetic; - // Don't know where else to place this constant // But this file seems appropriate #define PRELEVELTIME TICRATE // frames in tics +fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum); +void F_DoWipe(fademask_t *fademask); void F_WipeStartScreen(void); void F_WipeEndScreen(void); void F_RunWipe(UINT8 wipetype, boolean drawMenu); diff --git a/src/f_wipe.c b/src/f_wipe.c index 0497ae497..9dc414237 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -43,12 +43,12 @@ #define NOWIPE // do not enable wipe image post processing for ARM, SH and MIPS CPUs #endif -typedef struct fademask_s { +struct fademask_t { UINT8* mask; UINT16 width, height; size_t size; fixed_t xscale, yscale; -} fademask_t; +}; UINT8 wipedefs[NUMWIPEDEFS] = { 99, // wipe_credits_intermediate (0) @@ -83,9 +83,8 @@ UINT8 wipedefs[NUMWIPEDEFS] = { // SCREEN WIPE PACKAGE //-------------------------------------------------------------------------- -boolean WipeInAction = false; +UINT8 WipeInAction = 0; boolean WipeStageTitle = false; -INT32 lastwipetic = 0; #ifndef NOWIPE @@ -101,7 +100,7 @@ static fixed_t paldiv; * \param lump Lump name to get data from * \return fademask_t for lump */ -static fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) { +fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) { static char lumpname[9] = "FADEmmss"; static fademask_t fm = {NULL,0,0,0,0,0}; lumpnum_t lumpnum; @@ -186,7 +185,7 @@ static fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) { * * \param fademask pixels to change */ -static void F_DoWipe(fademask_t *fademask) +void F_DoWipe(fademask_t *fademask) { // Software mask wipe -- optimized; though it might not look like it! // Okay, to save you wondering *how* this is more optimized than the simpler @@ -360,63 +359,12 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu) (void)wipetype; (void)drawMenu; #else - tic_t nowtime; - UINT8 wipeframe = 0; - fademask_t *fmask; - paldiv = FixedDiv(257<mapmusrng = 0; header->numlaps = 0; header->raflags = 0; + header->empty = false; if (memcmp(dp, DEMOHEADER, 12)) return HEADER_BADMAGIC; @@ -687,7 +689,6 @@ static headerstatus_e G_ReadDemoHeader(UINT8 *dp, demoheader_t *header) raflag = false; break; - case 0x0001: // SRB2Kart 1.0.x (only staff ghosts supported) oldkart = kart = true; raflag = false; @@ -847,10 +848,16 @@ skipfiles: if (!kart) header->mapmusrng = READUINT8(dp); - // Sigh ... it's an empty demo. - if (*dp == DEMOMARKER || oldkart) + if (oldkart) goto end; + // Sigh ... it's an empty demo. + if (*dp == DEMOMARKER) + { + header->empty = true; + goto end; + } + // Load players that were in-game when the map started UINT8 playernum; header->playerdata = NULL; @@ -901,6 +908,10 @@ skipfiles: plr->followitem = !kart ? READUINT32(dp) : MT_NULL; } + // Sigh ... it's an empty demo. Again. + if (*dp == DEMOMARKER) + header->empty = true; + end: header->endofs = dp - startdp; return HEADER_OK; @@ -3390,15 +3401,15 @@ void G_DoPlayDemo(char *defdemoname) lumpnum_t l; char *n,*pdemoname; char msg[1024]; - - boolean spectator, bot; - UINT8 slots[MAXPLAYERS], kartspeed[MAXPLAYERS], kartweight[MAXPLAYERS], numslots = 0; + UINT8 pnum; #if defined(SKIPERRORS) && !defined(DEVELOP) boolean skiperrors = false; #endif + M_ClearMenus(true); G_InitDemoRewind(); + gameaction = ga_nothing; // No demo name means we're restarting the current demo if (defdemoname == NULL) @@ -3425,10 +3436,7 @@ void G_DoPlayDemo(char *defdemoname) if (P_SaveBufferFromFile(&demobuf, defdemoname) == false) { snprintf(msg, 1024, M_GetText("Failed to read file '%s'.\n"), defdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - gameaction = ga_nothing; - M_StartMessage(msg, NULL, MM_NOTHING); - return; + goto lumperror; } } // load demo resource from WAD @@ -3440,11 +3448,7 @@ void G_DoPlayDemo(char *defdemoname) if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR) { snprintf(msg, 1024, M_GetText("Failed to read lump '%s'.\n"), defdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - Z_Free(pdemoname); - gameaction = ga_nothing; - M_StartMessage(msg, NULL, MM_NOTHING); - return; + goto lumperror; } P_SaveBufferFromLump(&demobuf, l); @@ -3470,11 +3474,7 @@ void G_DoPlayDemo(char *defdemoname) if (mapnum >= nummapheaders || mapheaderinfo[mapnum]->lumpnum == LUMPERROR) { snprintf(msg, 1024, M_GetText("Failed to read lump '%s (couldn't find map %s)'.\n"), defdemoname, mapname); - CONS_Alert(CONS_ERROR, "%s", msg); - Z_Free(pdemoname); - gameaction = ga_nothing; - M_StartMessage(msg, NULL, MM_NOTHING); - return; + goto lumperror; } vRes = vres_GetMap(mapheaderinfo[mapnum]->lumpnum); @@ -3483,11 +3483,7 @@ void G_DoPlayDemo(char *defdemoname) if (vLump == NULL) { snprintf(msg, 1024, M_GetText("Failed to read lump '%s (couldn't find lump %s in %s)'.\n"), defdemoname, pdemoname, mapname); - CONS_Alert(CONS_ERROR, "%s", msg); - Z_Free(pdemoname); - gameaction = ga_nothing; - M_StartMessage(msg, NULL, MM_NOTHING); - return; + goto lumperror; } P_SaveBufferAlloc(&demobuf, vLump->size); @@ -3502,7 +3498,6 @@ void G_DoPlayDemo(char *defdemoname) } // read demo header - gameaction = ga_nothing; demo.playback = true; demo.buffer = &demobuf; @@ -3516,33 +3511,15 @@ void G_DoPlayDemo(char *defdemoname) case HEADER_BADMAGIC: snprintf(msg, 1024, M_GetText("%s is not a SRB2Kart replay file.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - Z_Free(pdemoname); - P_SaveBufferFree(&demobuf); - demo.playback = false; - demo.title = false; - M_StartMessage(msg, NULL, MM_NOTHING); - return; + goto headererror; case HEADER_BADVERSION: snprintf(msg, 1024, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - Z_Free(pdemoname); - P_SaveBufferFree(&demobuf); - demo.playback = false; - demo.title = false; - M_StartMessage(msg, NULL, MM_NOTHING); - return; + goto headererror; case HEADER_BADFORMAT: snprintf(msg, 1024, M_GetText("%s is the wrong type of recording and cannot be played.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - Z_Free(pdemoname); - P_SaveBufferFree(&demobuf); - demo.playback = false; - demo.title = false; - M_StartMessage(msg, NULL, MM_NOTHING); - return; + goto headererror; } demo.version = header.demoversion; @@ -3564,54 +3541,68 @@ void G_DoPlayDemo(char *defdemoname) G_LoadDemoExtraFiles(&header); else if (demo.ignorefiles) ;//G_SkipDemoExtraFiles(&demobuf.p); - else + else switch (G_CheckDemoExtraFiles(&header, false)) { - UINT8 error = G_CheckDemoExtraFiles(&header, false); + case DFILE_ERROR_NOTLOADED: + snprintf(msg, 1024, + M_GetText("Required files for this demo are not loaded.\n\nUse\n\"playdemo %s -addfiles\"\nto load them and play the demo.\n"), + pdemoname); + goto error; - if (error) + case DFILE_ERROR_OUTOFORDER: + snprintf(msg, 1024, + M_GetText("Required files for this demo are loaded out of order.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), + pdemoname); + goto error; + + case DFILE_ERROR_INCOMPLETEOUTOFORDER: + snprintf(msg, 1024, + M_GetText("Required files for this demo are not loaded, and some are out of order.\n\nUse\n\"playdemo %s -addfiles\"\nto load needed files and play the demo.\n"), + pdemoname); + goto error; + + case DFILE_ERROR_CANNOTLOAD: + snprintf(msg, 1024, + M_GetText("Required files for this demo cannot be loaded.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), + pdemoname); + goto error; + + case DFILE_ERROR_EXTRAFILES: + snprintf(msg, 1024, + M_GetText("You have additional files loaded beyond the demo's file list.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), + pdemoname); + goto error; + } + + // ...*map* not loaded? + if (!gamemap || (gamemap > nummapheaders) || !mapheaderinfo[gamemap-1] || mapheaderinfo[gamemap-1]->lumpnum == LUMPERROR) + { + snprintf(msg, 1024, M_GetText("%s features a course that is not currently loaded.\n"), pdemoname); + goto error; + } + + // Sigh ... it's an empty demo. + if (header.empty) + { + snprintf(msg, 1024, M_GetText("%s contains no data to be played.\n"), pdemoname); + goto error; + } + + // extra checks for RA replays + if (demoflags & DF_ATTACKMASK) + { + const char *reason = NULL; + if (header.numplayers != 1) + reason = "multiple players"; + else if (header.playerdata[0].flags & DEMO_SPECTATOR) + reason = "spectators"; + else if (header.playerdata[0].flags & DEMO_BOT) + reason = "bots"; + + if (reason) { - switch (error) - { - case DFILE_ERROR_NOTLOADED: - snprintf(msg, 1024, - M_GetText("Required files for this demo are not loaded.\n\nUse\n\"playdemo %s -addfiles\"\nto load them and play the demo.\n"), - pdemoname); - break; - - case DFILE_ERROR_OUTOFORDER: - snprintf(msg, 1024, - M_GetText("Required files for this demo are loaded out of order.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), - pdemoname); - break; - - case DFILE_ERROR_INCOMPLETEOUTOFORDER: - snprintf(msg, 1024, - M_GetText("Required files for this demo are not loaded, and some are out of order.\n\nUse\n\"playdemo %s -addfiles\"\nto load needed files and play the demo.\n"), - pdemoname); - break; - - case DFILE_ERROR_CANNOTLOAD: - snprintf(msg, 1024, - M_GetText("Required files for this demo cannot be loaded.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), - pdemoname); - break; - - case DFILE_ERROR_EXTRAFILES: - snprintf(msg, 1024, - M_GetText("You have additional files loaded beyond the demo's file list.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), - pdemoname); - break; - } - - CONS_Alert(CONS_ERROR, "%s", msg); - Z_Free(pdemoname); - P_SaveBufferFree(&demobuf); - G_FreeDemoHeader(&header); - demo.playback = false; - demo.title = false; - if (!CON_Ready()) // In the console they'll just see the notice there! No point pulling them out. - M_StartMessage(msg, NULL, MM_NOTHING); - return; + snprintf(msg, 1024, M_GetText("%s is a Record Attack replay with %s, and is thus invalid.\n"), pdemoname, reason); + goto error; } } @@ -3627,20 +3618,6 @@ void G_DoPlayDemo(char *defdemoname) if (modeattacking != ATTACKING_TIME && modeattacking != ATTACKING_ITEMBREAK) modeattacking = ATTACKING_NONE; // is this really necessary? - // ...*map* not loaded? - if (!gamemap || (gamemap > nummapheaders) || !mapheaderinfo[gamemap-1] || mapheaderinfo[gamemap-1]->lumpnum == LUMPERROR) - { - snprintf(msg, 1024, M_GetText("%s features a course that is not currently loaded.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - Z_Free(pdemoname); - P_SaveBufferFree(&demobuf); - G_FreeDemoHeader(&header); - demo.playback = false; - demo.title = false; - M_StartMessage(msg, NULL, MM_NOTHING); - return; - } - // net var data CV_LoadDemoVars(&header); @@ -3656,20 +3633,6 @@ void G_DoPlayDemo(char *defdemoname) // Load "mapmusrng" used for altmusic selection mapmusrng = header.mapmusrng; - // Sigh ... it's an empty demo. - if (header.numplayers == 0) - { - snprintf(msg, 1024, M_GetText("%s contains no data to be played.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - Z_Free(pdemoname); - P_SaveBufferFree(&demobuf); - G_FreeDemoHeader(&header); - demo.playback = false; - demo.title = false; - M_StartMessage(msg, NULL, MM_NOTHING); - return; - } - Z_Free(pdemoname); memset(&oldcmd,0,sizeof(oldcmd)); @@ -3699,48 +3662,16 @@ void G_DoPlayDemo(char *defdemoname) memset(camera,0,sizeof(camera)); // reset freecam // Load players that were in-game when the map started - for (UINT8 pnum = 0; pnum < header.numplayers; pnum++) + for (pnum = 0; pnum < header.numplayers; pnum++) { demoplayer_t *plr = &header.playerdata[pnum]; UINT8 p = plr->playernum; - spectator = !!(plr->flags & DEMO_SPECTATOR); - bot = !!(plr->flags & DEMO_BOT); - - if ((spectator || bot)) - { - if (modeattacking) - { - snprintf(msg, 1024, M_GetText("%s is a Record Attack replay with %s, and is thus invalid.\n"), pdemoname, (bot ? "bots" : "spectators")); - CONS_Alert(CONS_ERROR, "%s", msg); - Z_Free(pdemoname); - P_SaveBufferFree(&demobuf); - demo.playback = false; - demo.title = false; - M_StartMessage(msg, NULL, MM_NOTHING); - return; - } - } - - slots[numslots] = p; - numslots++; - - if (modeattacking && numslots > 1) - { - snprintf(msg, 1024, M_GetText("%s is a Record Attack replay with multiple players, and is thus invalid.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - Z_Free(pdemoname); - P_SaveBufferFree(&demobuf); - demo.playback = false; - demo.title = false; - M_StartMessage(msg, NULL, MM_NOTHING); - return; - } if (!playeringame[displayplayers[0]] || players[displayplayers[0]].spectator) displayplayers[0] = consoleplayer = serverplayer = p; G_AddPlayer(p, p); - players[p].spectator = spectator; + players[p].spectator = !!(plr->flags & DEMO_SPECTATOR); if (plr->flags & DEMO_KICKSTART) players[p].pflags |= PF_KICKSTARTACCEL; @@ -3754,7 +3685,7 @@ void G_DoPlayDemo(char *defdemoname) K_UpdateShrinkCheat(&players[p]); - if ((players[p].bot = bot) == true) + if ((players[p].bot = !!(plr->flags & DEMO_BOT)) == true) { players[p].botvars.difficulty = plr->bot.difficulty; players[p].botvars.diffincrease = plr->bot.diffincrease; // needed to avoid having to duplicate logic @@ -3797,12 +3728,10 @@ void G_DoPlayDemo(char *defdemoname) // Power Levels clientpowerlevels[p][gametype == GT_BATTLE ? PWRLV_BATTLE : PWRLV_RACE] = plr->powerlevel; - // Kart stats, temporarily - kartspeed[p] = plr->kartspeed; - kartweight[p] = plr->kartweight; + // Kart stats (set later) if (stricmp(skins[players[p].skin].name, plr->skin) != 0) - FindClosestSkinForStats(p, kartspeed[p], kartweight[p]); + FindClosestSkinForStats(p, plr->kartspeed, plr->kartweight); // Followitem players[p].followitem = plr->followitem; @@ -3824,10 +3753,10 @@ void G_DoPlayDemo(char *defdemoname) if (demo.title) { splitscreen = M_RandomKey(6)-1; - splitscreen = min(min(3, numslots-1), splitscreen); // Bias toward 1p and 4p views + splitscreen = min(min(3, header.numplayers-1), splitscreen); // Bias toward 1p and 4p views for (i = 0; i <= splitscreen; i++) - G_ResetView(i+1, slots[M_RandomKey(numslots)], false); + G_ResetView(i+1, header.playerdata[M_RandomKey(header.numplayers)].playernum, false); } R_ExecuteSetViewSize(); @@ -3835,19 +3764,33 @@ void G_DoPlayDemo(char *defdemoname) P_SetRandSeed(header.randseed); G_InitNew(demoflags & DF_ENCORE, gamemap, true, true, false); // Doesn't matter whether you reset or not here, given changes to resetplayer. - for (i = 0; i < MAXPLAYERS; i++) + for (pnum = 0; pnum < header.numplayers; pnum++) { // oldghost init doesn't work here, players aren't immediately spawned anymore // Set saved attribute values // No cheat checking here, because even if they ARE wrong... // it would only break the replay if we clipped them. - players[i].kartspeed = kartspeed[i]; - players[i].kartweight = kartweight[i]; + demoplayer_t *plr = &header.playerdata[pnum]; + players[plr->playernum].kartspeed = plr->kartspeed; + players[plr->playernum].kartweight = plr->kartweight; } demo.deferstart = true; G_FreeDemoHeader(&header); + return; + +error: + G_FreeDemoHeader(&header); +headererror: + P_SaveBufferFree(&demobuf); +lumperror: + CONS_Alert(CONS_ERROR, "%s", msg); + Z_Free(pdemoname); + demo.playback = false; + demo.title = false; + if (!CON_Ready()) // In the console they'll just see the notice there! No point pulling them out. + M_StartMessage(msg, NULL, MM_NOTHING); } void G_SetupDemoPlayer(INT32 i) @@ -3956,7 +3899,7 @@ void G_AddGhost(char *defdemoname) return; } - if (header.numplayers == 0) + if (header.empty) { CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Replay is empty.\n"), pdemoname); Z_Free(pdemoname); diff --git a/src/g_game.c b/src/g_game.c index 31214febf..9f845cf56 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1507,30 +1507,7 @@ void G_StartTitleCard(void) void G_PreLevelTitleCard(void) { #ifndef NOWIPE - tic_t strtime = I_GetTime(); - tic_t endtime = strtime + (PRELEVELTIME*NEWTICRATERATIO); - tic_t nowtime = strtime; - tic_t lasttime = strtime; - while (nowtime < endtime) - { - // draw loop - ST_runTitleCard(); - ST_preLevelTitleCardDrawer(); - I_FinishUpdate(); // page flip or blit buffer - NetKeepAlive(); // Prevent timeouts - - if (moviemode) - M_SaveFrame(); - if (takescreenshot) // Only take screenshots after drawing. - M_DoScreenShot(); - - while (!((nowtime = I_GetTime()) - lasttime)) - { - I_Sleep(cv_sleep.value); - I_UpdateTime(cv_timescale.value); - } - lasttime = nowtime; - } + D_WipeLoop(WIPELOOP_TITLECARD, 0, false); #endif } @@ -2300,6 +2277,7 @@ void G_Ticker(boolean run) case GS_DEDICATEDSERVER: case GS_NULL: + case FORCEWIPE: break; // do nothing } diff --git a/src/g_state.h b/src/g_state.h index 94f2414bb..678ba0a5d 100644 --- a/src/g_state.h +++ b/src/g_state.h @@ -22,6 +22,7 @@ extern "C" { // the current state of the game typedef enum { + FORCEWIPE = -2, // HACK for menu fading while titlemapinaction; skips the level check GS_NULL = 0, // At beginning. // Fadable gamestates diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 4bd25c826..ff71e987f 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -1083,6 +1083,9 @@ void HU_Ticker(void) } } + if (WipeInAction) + return; + if (cechotimer) cechotimer--; @@ -2136,6 +2139,9 @@ void HU_Drawer(void) HU_drawMiniChat(); // draw messages in a cool fashion. } + if (WipeInAction) + return; + if (cechotimer) HU_DrawCEcho(); diff --git a/src/m_menu.c b/src/m_menu.c index 772846fc0..a8bf85a10 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1187,7 +1187,7 @@ static boolean M_ChangeStringCvar(INT32 choice) // lock out further input in a tic when important buttons are pressed // (in other words -- stop bullshit happening by mashing buttons in fades) -static boolean noFurtherInput = false; +static INT32 noFurtherInput = 0; static void Command_Manual_f(void) { @@ -1256,6 +1256,30 @@ static boolean M_DemoBinds(INT32 ch) return true; } +// if the menu is being used during a wipe, running routines can be dangerous! +// buffer a single input, then wait for the wipe to end +static boolean M_WipeBuffer(INT32 ch, menufunc_f *routine) +{ + if (!WipeInAction) + return false; + + // these routines are allowed to run during wipes + // solely to make the menus less annoying to use + if (routine == NULL + || (routine == MR_SelectableClearMenus && !currentMenu->quitroutine) + || routine == MR_Options + || routine == MR_SetupMultiPlayer + ) + return false; + + noFurtherInput = ch; + S_StartSound(NULL, sfx_kc50); + return true; +} + +// use this when routine being NULL isn't a free pass +static INT32 MR_Dummy(INT32 ch) { return true; } + // // M_Responder // @@ -1435,10 +1459,12 @@ boolean M_Responder(event_t *ev) { if (ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE || ch == KEY_ENTER) { + if (M_WipeBuffer(ch, MR_Dummy)) + return true; if (messagebox.routine) messagebox.routine(ch); messagebox.active = false; - noFurtherInput = true; + noFurtherInput = -1; } } else @@ -1453,7 +1479,7 @@ boolean M_Responder(event_t *ev) // F-Keys if (!menustack[0]) { - noFurtherInput = true; + noFurtherInput = -1; switch (ch) { @@ -1469,7 +1495,7 @@ boolean M_Responder(event_t *ev) return true; case KEY_F4: // Sound Volume - if (modeattacking) + if (modeattacking || WipeInAction) return true; M_StartControlPanel(); M_EnterMenu(MN_OP_MAIN, true, 0); @@ -1478,7 +1504,7 @@ boolean M_Responder(event_t *ev) return true; case KEY_F5: // Video Mode - if (modeattacking) + if (modeattacking || WipeInAction) return true; M_StartControlPanel(); M_EnterMenu(MN_OP_MAIN, true, 0); @@ -1490,7 +1516,7 @@ boolean M_Responder(event_t *ev) return true; case KEY_F7: // Options - if (modeattacking) + if (modeattacking || WipeInAction) return true; M_StartControlPanel(); M_EnterMenu(MN_OP_MAIN, true, 0); @@ -1519,13 +1545,15 @@ boolean M_Responder(event_t *ev) M_StartControlPanel(); return true; } - noFurtherInput = false; // turns out we didn't care + noFurtherInput = 0; // turns out we didn't care return false; } // Handle menuitems which need a specific key handling if (currentMenu->keyhandler) { + if (M_WipeBuffer(ch, currentMenu->keyhandler)) + return true; if (shiftdown && ch >= 32 && ch <= 127) ch = shiftxform[ch]; if (currentMenu->keyhandler(ch)) @@ -1537,8 +1565,14 @@ boolean M_Responder(event_t *ev) // G: nevermind we have combi menuitems now menuitem_t *item = currentMenu->numitems ? ¤tMenu->menuitems[itemOn] : NULL; - if (item && item->cvar && !item->cvar->PossibleValue && M_ChangeStringCvar(ch)) - return true; + // string cvars accept any keyboard input + if (item && item->cvar && !item->cvar->PossibleValue) + { + if (item->cvar->flags & CV_CALL && M_WipeBuffer(ch, MR_Dummy)) + return true; + if (M_ChangeStringCvar(ch)) + return true; + } if (menustack[0] == MN_PLAYBACK && !con_destlines) { @@ -1589,6 +1623,9 @@ boolean M_Responder(event_t *ev) if (!item || !(item->cvar || item->status & IT_ARROWS)) return true; + if (M_WipeBuffer(ch, item->cvar ? (item->cvar->flags & CV_CALL ? MR_Dummy : NULL) : item->routine)) + return true; + if (menustack[0] != MN_OP_SOUND || itemOn > 3) S_StartSound(NULL, sfx_menu1); @@ -1603,7 +1640,7 @@ boolean M_Responder(event_t *ev) if (!item) return true; - noFurtherInput = true; + noFurtherInput = -1; currentMenu->lastOn = itemOn; if (menustack[0] == MN_PLAYBACK) @@ -1620,25 +1657,34 @@ boolean M_Responder(event_t *ev) if (item->submenu) { + if (M_WipeBuffer(ch, menudefs[item->submenu].enterroutine)) + return true; S_StartSound(NULL, sfx_menu1); M_EnterMenu(item->submenu, true, argument); } else if (item->routine) { + if (M_WipeBuffer(ch, item->routine)) + return true; S_StartSound(NULL, sfx_menu1); item->routine(argument); } else if (item->cvar && item->cvar->PossibleValue) // not for string cvars! { + if (item->cvar->flags & CV_CALL && M_WipeBuffer(ch, MR_Dummy)) + return true; S_StartSound(NULL, sfx_menu1); M_ChangeCvar(item, 1); // right arrow } return true; case KEY_ESCAPE: - noFurtherInput = true; + noFurtherInput = -1; currentMenu->lastOn = itemOn; + if (M_WipeBuffer(ch, currentMenu->quitroutine)) + return true; + //If we entered the game search menu, but didn't enter a game, //make sure the game doesn't still think we're in a netgame. if (!Playing() && netgame && multiplayer) @@ -1659,6 +1705,9 @@ boolean M_Responder(event_t *ev) || item->cvar == &cv_dummymultiplayer) return true; + if (item->cvar->flags & CV_CALL && M_WipeBuffer(ch, MR_Dummy)) + return true; + if (menustack[0] != MN_OP_SOUND || itemOn > 3) S_StartSound(NULL, sfx_menu1); M_ResetCvar(item); @@ -1848,7 +1897,9 @@ void M_ClearMenus(boolean callexitmenufunc) currentMenu->quitroutine(0); // Save the config file. I'm sick of crashing the game later and losing all my changes! - COM_BufAddText(va("saveconfig \"%s\" -silent\n", configfile)); + char buf[sizeof(configfile) + 50]; + sprintf(buf, "saveconfig \"%s\" -silent\n", configfile); + COM_BufAddText(buf); if (currentMenu->exitwipe >= 0) { @@ -1960,8 +2011,18 @@ boolean M_MouseNeeded(void) // void M_Ticker(void) { - // reset input trigger - noFurtherInput = false; + // send buffered input from wipe? + if (noFurtherInput > 0 && !WipeInAction) + { + event_t fake; + fake.device = 0; + fake.type = ev_keydown; + fake.data1 = noFurtherInput; + D_PostEvent(&fake); + noFurtherInput = 0; + } + else if (noFurtherInput == -1 || !WipeInAction) + noFurtherInput = 0; if (dedicated) return; @@ -2695,7 +2756,9 @@ void MD_DrawGenericMenu(void) // DRAW THE SKULL CURSOR if (M_ItemSelectable(¤tMenu->menuitems[itemOn])) - V_DrawScaledPatch(cursorx, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); + V_DrawScaledPatch(cursorx, cursory, (noFurtherInput > 0 ? V_TRANSLUCENT : 0), W_CachePatchName("M_CURSOR", PU_CACHE)); + if (noFurtherInput > 0) + V_DrawSmallString(cursorx, cursory+3, 0, "WAIT"); x = currentMenu->x - 20 + currentMenu->cursoroffset; if (cliptop) @@ -4220,11 +4283,9 @@ INT32 MR_HutStartReplay(INT32 choice) { (void)choice; - M_ClearMenus(false); - demo.loadfiles = M_IsItemOn(MN_MISC_REPLAYSTART, "LOADWATCH"); - demo.ignorefiles = !M_IsItemOn(MN_MISC_REPLAYSTART, "LOADWATCH"); - - G_DoPlayDemo(demolist[dir_on[menudepthleft]].filepath); + COM_BufAddText(va("playdemo %s %s", + demolist[dir_on[menudepthleft]].filepath + strlen(srb2home) + 1, // dumb hack + M_IsItemOn(MN_MISC_REPLAYSTART, "LOADWATCH") ? "-addfiles" : "-force")); return true; } @@ -5733,9 +5794,7 @@ INT32 MR_ReplayStaff(INT32 choice) if (l == LUMPERROR) return false; - M_ClearMenus(true); - demo.loadfiles = false; demo.ignorefiles = true; // Just assume that record attack replays have the files needed - G_DoPlayDemo(va("%sS%02u",G_BuildMapName(cv_nextmap.value),cv_dummystaff.value)); + COM_BufAddText(va("playdemo %sS%02u -force", G_BuildMapName(cv_nextmap.value), cv_dummystaff.value)); return true; } @@ -5768,8 +5827,6 @@ INT32 MR_ReplayTimeAttack(INT32 arg) { const char *which; char *gamemode = M_AppendGametypeAndModName(); - M_ClearMenus(true); - demo.loadfiles = false; demo.ignorefiles = true; // Just assume that record attack replays have the files needed switch(arg) { default: @@ -5784,12 +5841,12 @@ INT32 MR_ReplayTimeAttack(INT32 arg) break; case 3: // guest // srb2/replay/main/map01-guest.lmp - G_DoPlayDemo(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), gamemode)); + COM_BufAddText(va("playdemo media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-guest.lmp -force", timeattackfolder, G_BuildMapName(cv_nextmap.value), gamemode)); Z_Free(gamemode); return true; } // srb2/replay/main/map01-sonic-time-best.lmp - G_DoPlayDemo(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value].name, gamemode, which)); + COM_BufAddText(va("playdemo media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s-%s.lmp -force", timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value].name, gamemode, which)); Z_Free(gamemode); return true; } diff --git a/src/p_setup.c b/src/p_setup.c index 8b094ab40..e833a0fb1 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8637,8 +8637,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // This is handled BEFORE sounds are stopped. else if (encoremode && !prevencoremode && !demo.rewinding) { - tic_t locstarttime, endtime, nowtime; - if (rendermode != render_none) { S_StopMusic(); // er, about that... @@ -8663,25 +8661,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) F_RunWipe(wipedefs[wipe_level_final], false); } - locstarttime = nowtime = lastwipetic; - endtime = locstarttime + (3*TICRATE)/2; - // Hold on white for extra effect. - while (nowtime < endtime) - { - // wait loop - while (!((nowtime = I_GetTime()) - lastwipetic)) - { - I_Sleep(cv_sleep.value); - I_UpdateTime(cv_timescale.value); - } - lastwipetic = nowtime; - if (moviemode) // make sure we save frames for the white hold too - M_SaveFrame(); - - // Keep the network alive - NetKeepAlive(); - } + D_WipeLoop(WIPELOOP_ENCORE, 0, false); ranspecialwipe = 1; } @@ -9031,12 +9012,14 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) levelloading = false; } - if (rendermode != render_none && reloadinggamestate == false) + if (reloadinggamestate == false) { - - R_ResetViewInterpolation(0); - R_ResetViewInterpolation(0); - R_UpdateMobjInterpolators(); + if (rendermode != render_none) + { + R_ResetViewInterpolation(0); + R_ResetViewInterpolation(0); + R_UpdateMobjInterpolators(); + } // Title card! G_StartTitleCard(); @@ -9045,6 +9028,10 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) if (WipeStageTitle && ranspecialwipe != 2 && fromnetsave == false) { G_PreLevelTitleCard(); + + // don't do a fade-in if we ran the title card, it trips up dedis! + // normal clients never see the fade-in, and it causes a very brief lag spike + wipegamestate = gamestate; } } diff --git a/src/typedef.h b/src/typedef.h index e5935d32a..424858d4a 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -121,6 +121,9 @@ TYPEDEF (cupheader_t); TYPEDEF (exitcondition_t); TYPEDEF (mapheader_lighting_t); +// f_finale.h +TYPEDEF (fademask_t); + // font.h TYPEDEF (font_t);