Merge pull request 'Wipes refactor' (#77) from bigwipes into blankart-dev

Reviewed-on: https://codeberg.org/NepDisk/blankart/pulls/77
This commit is contained in:
NepDisk 2025-08-10 00:17:04 +02:00
commit 9a3a4cfc78
13 changed files with 465 additions and 418 deletions

View file

@ -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"));

View file

@ -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;
}
}

View file

@ -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,

View file

@ -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;

View file

@ -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);

View file

@ -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<<FRACBITS, 11<<FRACBITS);
// Init the wipe
WipeInAction = true;
wipe_scr = screens[0];
// lastwipetic should either be 0 or the tic we last wiped
// on for fade-to-black
for (;;)
{
// get fademask first so we can tell if it exists or not
fmask = F_GetFadeMask(wipetype, wipeframe++);
if (!fmask)
break;
// wait loop
while (!((nowtime = I_GetTime()) - lastwipetic))
{
I_Sleep(cv_sleep.value);
I_UpdateTime(cv_timescale.value);
}
lastwipetic = nowtime;
#ifdef HWRENDER
if (rendermode == render_opengl)
HWR_DoWipe(wipetype, wipeframe-1); // 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);
I_OsPolling();
I_UpdateNoBlit();
if (drawMenu && rendermode != render_none)
{
#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(); // page flip or blit buffer
if (moviemode)
M_SaveFrame();
NetKeepAlive(); // Update the network so we don't cause timeouts
}
WipeInAction = false;
D_WipeLoop(WIPELOOP_RUNWIPE, wipetype, drawMenu);
#endif
}

View file

@ -276,6 +276,7 @@ typedef struct
UINT8 numplayers;
demoplayer_t *playerdata;
boolean empty;
UINT32 endofs;
} demoheader_t;
@ -669,6 +670,7 @@ static headerstatus_e G_ReadDemoHeader(UINT8 *dp, demoheader_t *header)
header->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);

View file

@ -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
}

View file

@ -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

View file

@ -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();

View file

@ -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 ? &currentMenu->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(&currentMenu->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;
}

View file

@ -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;
}
}

View file

@ -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);