Merge branch 'replay-kms' into 'master'

demo improvements (why)

See merge request kart-krew-dev/ring-racers-internal!2598
This commit is contained in:
Oni VelocitOni 2025-06-02 21:30:45 +00:00
commit 03d9430bb9
29 changed files with 883 additions and 767 deletions

View file

@ -7560,66 +7560,6 @@ tic_t GetLag(INT32 node)
return gametic - nettics[node]; return gametic - nettics[node];
} }
#define REWIND_POINT_INTERVAL 4*TICRATE + 16
rewind_t *rewindhead;
void CL_ClearRewinds(void)
{
rewind_t *head;
while ((head = rewindhead))
{
rewindhead = rewindhead->next;
free(head);
}
}
rewind_t *CL_SaveRewindPoint(size_t demopos)
{
savebuffer_t save = {0};
rewind_t *rewind;
if (rewindhead && rewindhead->leveltime + REWIND_POINT_INTERVAL > leveltime)
return NULL;
rewind = (rewind_t *)malloc(sizeof (rewind_t));
if (!rewind)
return NULL;
P_SaveBufferFromExisting(&save, rewind->savebuffer, NETSAVEGAMESIZE);
P_SaveNetGame(&save, false);
rewind->leveltime = leveltime;
rewind->next = rewindhead;
rewind->demopos = demopos;
rewindhead = rewind;
return rewind;
}
rewind_t *CL_RewindToTime(tic_t time)
{
savebuffer_t save = {0};
rewind_t *rewind;
while (rewindhead && rewindhead->leveltime > time)
{
rewind = rewindhead->next;
free(rewindhead);
rewindhead = rewind;
}
if (!rewindhead)
return NULL;
P_SaveBufferFromExisting(&save, rewindhead->savebuffer, NETSAVEGAMESIZE);
P_LoadNetGame(&save, false);
wipegamestate = gamestate; // No fading back in!
timeinmap = leveltime;
return rewindhead;
}
void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest) void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest)
{ {
#ifdef NOMD5 #ifdef NOMD5

View file

@ -712,21 +712,6 @@ extern boolean hu_stopped;
// SRB2Kart // SRB2Kart
// //
struct rewind_t {
UINT8 savebuffer[NETSAVEGAMESIZE];
tic_t leveltime;
size_t demopos;
ticcmd_t oldcmd[MAXPLAYERS];
mobj_t oldghost[MAXPLAYERS];
rewind_t *next;
};
void CL_ClearRewinds(void);
rewind_t *CL_SaveRewindPoint(size_t demopos);
rewind_t *CL_RewindToTime(tic_t time);
void HandleSigfail(const char *string); void HandleSigfail(const char *string);
void DoSayPacket(SINT8 target, UINT8 flags, UINT8 source, char *message); void DoSayPacket(SINT8 target, UINT8 flags, UINT8 source, char *message);

View file

@ -742,9 +742,6 @@ static bool D_Display(bool world)
if (forcerefresh && G_GamestateUsesLevel() == false) if (forcerefresh && G_GamestateUsesLevel() == false)
V_SetPalette(0); V_SetPalette(0);
if (demo.rewinding)
V_DrawFadeScreen(TC_RAINBOW, (leveltime & 0x20) ? SKINCOLOR_PASTEL : SKINCOLOR_MOONSET);
// vid size change is now finished if it was on... // vid size change is now finished if it was on...
vid.recalc = 0; vid.recalc = 0;
@ -936,7 +933,7 @@ void D_SRB2Loop(void)
realtics = entertic - oldentertics; realtics = entertic - oldentertics;
oldentertics = entertic; oldentertics = entertic;
if (demo.playback && gamestate == GS_LEVEL) if (demo.playback && gamestate == GS_LEVEL && demo.simplerewind == DEMO_REWIND_OFF)
{ {
// Nicer place to put this. // Nicer place to put this.
realtics = realtics * cv_playbackspeed.value; realtics = realtics * cv_playbackspeed.value;
@ -1216,6 +1213,7 @@ void D_ClearState(void)
// Reset GP and roundqueue // Reset GP and roundqueue
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
memset(&roundqueue, 0, sizeof(struct roundqueue)); memset(&roundqueue, 0, sizeof(struct roundqueue));
memset(&menuqueue, 0, sizeof(struct menuqueue));
// empty some other semi-important state // empty some other semi-important state
maptol = 0; maptol = 0;
@ -1248,7 +1246,8 @@ void D_ClearState(void)
if (gamedata && gamedata->deferredsave) if (gamedata && gamedata->deferredsave)
G_SaveGameData(); G_SaveGameData();
K_UnsetDialogue(); P_FreeLevelState();
P_InvalidateThinkersWithoutInit();
G_SetGamestate(GS_NULL); G_SetGamestate(GS_NULL);
wipegamestate = GS_NULL; wipegamestate = GS_NULL;

View file

@ -2984,7 +2984,7 @@ static void Command_RestartLevel(void)
D_MapChange(gamemap, g_lastgametype, newencore, false, 0, false, false); D_MapChange(gamemap, g_lastgametype, newencore, false, 0, false, false);
} }
static void Handle_MapQueueSend(UINT16 newmapnum, UINT16 newgametype, boolean newencoremode) void Handle_MapQueueSend(UINT16 newmapnum, UINT16 newgametype, boolean newencoremode)
{ {
UINT8 flags = 0; UINT8 flags = 0;

View file

@ -206,6 +206,7 @@ size_t WeaponPref_Parse(const UINT8 *p, INT32 playernum);
void D_SendPlayerConfig(UINT8 n); void D_SendPlayerConfig(UINT8 n);
void Command_ExitGame_f(void); void Command_ExitGame_f(void);
void Command_Retry_f(void); void Command_Retry_f(void);
void Handle_MapQueueSend(UINT16 newmapnum, UINT16 newgametype, boolean newencoremode);
boolean G_GamestateUsesExitLevel(void); boolean G_GamestateUsesExitLevel(void);
void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore
void D_MapChange(UINT16 pmapnum, INT32 pgametype, boolean pencoremode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pforcespecialstage); void D_MapChange(UINT16 pmapnum, INT32 pgametype, boolean pencoremode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pforcespecialstage);

View file

@ -404,7 +404,10 @@ class TiccmdBuilder
map(gc_item, BT_ATTACK); // fire map(gc_item, BT_ATTACK); // fire
map(gc_lookback, BT_LOOKBACK); // rear view map(gc_lookback, BT_LOOKBACK); // rear view
map(gc_respawn, BT_RESPAWN | (freecam() ? 0 : BT_EBRAKEMASK)); // respawn if (!modeattacking)
{
map(gc_respawn, BT_RESPAWN | (freecam() ? 0 : BT_EBRAKEMASK)); // respawn
}
map(gc_vote, BT_VOTE); // mp general function button map(gc_vote, BT_VOTE); // mp general function button
// lua buttons a thru c // lua buttons a thru c

View file

@ -270,22 +270,31 @@ static ticcmd_t oldcmd[MAXPLAYERS];
static mobj_t oldghost[MAXPLAYERS]; static mobj_t oldghost[MAXPLAYERS];
boolean G_ConsiderEndingDemoWrite(void)
{
// chill, we reserved extra memory so it's
// "safe" to have written a bit past the end
if (demobuf.p < demobuf.end)
return false;
G_CheckDemoStatus();
return true;
}
boolean G_ConsiderEndingDemoRead(void)
{
if (*demobuf.p != DEMOMARKER)
return false;
G_CheckDemoStatus();
return true;
}
void G_ReadDemoExtraData(void) void G_ReadDemoExtraData(void)
{ {
INT32 p, extradata, i; INT32 p, extradata, i;
char name[64]; char name[64];
static_assert(sizeof name >= std::max({MAXPLAYERNAME+1u, SKINNAMESIZE+1u, MAXCOLORNAME+1u})); static_assert(sizeof name >= std::max({MAXPLAYERNAME+1u, SKINNAMESIZE+1u, MAXCOLORNAME+1u}));
if (leveltime > starttime)
{
rewind_t *rewind = CL_SaveRewindPoint(demobuf.p - demobuf.buffer);
if (rewind)
{
memcpy(rewind->oldcmd, oldcmd, sizeof (oldcmd));
memcpy(rewind->oldghost, oldghost, sizeof (oldghost));
}
}
memset(name, '\0', sizeof name); memset(name, '\0', sizeof name);
p = READUINT8(demobuf.p); p = READUINT8(demobuf.p);
@ -460,13 +469,6 @@ void G_ReadDemoExtraData(void)
p = READUINT8(demobuf.p); p = READUINT8(demobuf.p);
} }
if (!(demoflags & DF_GHOST) && *demobuf.p == DEMOMARKER)
{
// end of demo data stream
G_CheckDemoStatus();
return;
}
} }
void G_WriteDemoExtraData(void) void G_WriteDemoExtraData(void)
@ -623,13 +625,6 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
} }
G_CopyTiccmd(cmd, &oldcmd[playernum], 1); G_CopyTiccmd(cmd, &oldcmd[playernum], 1);
if (!(demoflags & DF_GHOST) && *demobuf.p == DEMOMARKER)
{
// end of demo data stream
G_CheckDemoStatus();
return;
}
} }
void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
@ -739,14 +734,6 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
WRITEUINT16(botziptic_p, botziptic); WRITEUINT16(botziptic_p, botziptic);
} }
// attention here for the ticcmd size!
// latest demos with mouse aiming byte in ticcmd
if (!(demoflags & DF_GHOST) && ziptic_p > demobuf.end - 9)
{
G_CheckDemoStatus(); // no more space
return;
}
} }
void G_GhostAddFlip(INT32 playernum) void G_GhostAddFlip(INT32 playernum)
@ -794,8 +781,11 @@ void G_GhostAddHit(INT32 playernum, mobj_t *victim)
void G_WriteAllGhostTics(void) void G_WriteAllGhostTics(void)
{ {
boolean toobig = false;
INT32 i, counter = leveltime; INT32 i, counter = leveltime;
if (!demobuf.p || !(demoflags & DF_GHOST))
return; // No ghost data to write.
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
if (!playeringame[i] || players[i].spectator) if (!playeringame[i] || players[i].spectator)
@ -811,22 +801,9 @@ void G_WriteAllGhostTics(void)
WRITEUINT8(demobuf.p, i); WRITEUINT8(demobuf.p, i);
G_WriteGhostTic(players[i].mo, i); G_WriteGhostTic(players[i].mo, i);
// attention here for the ticcmd size!
// latest demos with mouse aiming byte in ticcmd
if (demobuf.p >= demobuf.end - (13 + 9 + 9))
{
toobig = true;
break;
}
} }
WRITEUINT8(demobuf.p, 0xFF); WRITEUINT8(demobuf.p, 0xFF);
if (toobig)
{
G_CheckDemoStatus(); // no more space
return;
}
} }
void G_WriteGhostTic(mobj_t *ghost, INT32 playernum) void G_WriteGhostTic(mobj_t *ghost, INT32 playernum)
@ -835,11 +812,6 @@ void G_WriteGhostTic(mobj_t *ghost, INT32 playernum)
UINT8 *ziptic_p; UINT8 *ziptic_p;
UINT32 i; UINT32 i;
if (!demobuf.p)
return;
if (!(demoflags & DF_GHOST))
return; // No ghost data to write.
ziptic_p = demobuf.p++; // the ziptic, written at the end of this function ziptic_p = demobuf.p++; // the ziptic, written at the end of this function
#define MAXMOM (0xFFFF<<8) #define MAXMOM (0xFFFF<<8)
@ -1061,7 +1033,7 @@ void G_ConsAllGhostTics(void)
{ {
UINT8 p; UINT8 p;
if (!demobuf.p || !demo.deferstart) if (!demobuf.p || !(demoflags & DF_GHOST) || !demo.deferstart)
return; return;
p = READUINT8(demobuf.p); p = READUINT8(demobuf.p);
@ -1071,13 +1043,6 @@ void G_ConsAllGhostTics(void)
G_ConsGhostTic(p); G_ConsGhostTic(p);
p = READUINT8(demobuf.p); p = READUINT8(demobuf.p);
} }
if (*demobuf.p == DEMOMARKER)
{
// end of demo data stream
G_CheckDemoStatus();
return;
}
} }
// Uses ghost data to do consistency checks on your position. // Uses ghost data to do consistency checks on your position.
@ -1089,9 +1054,6 @@ void G_ConsGhostTic(INT32 playernum)
mobj_t *testmo; mobj_t *testmo;
UINT32 syncleeway; UINT32 syncleeway;
if (!(demoflags & DF_GHOST))
return; // No ghost data to use.
testmo = players[playernum].mo; testmo = players[playernum].mo;
// Grab ghost data. // Grab ghost data.
@ -1282,13 +1244,6 @@ void G_ConsGhostTic(INT32 playernum)
} }
} }
} }
if (*demobuf.p == DEMOMARKER)
{
// end of demo data stream
G_CheckDemoStatus();
return;
}
} }
void G_GhostTicker(void) void G_GhostTicker(void)
@ -1309,11 +1264,31 @@ void G_GhostTicker(void)
continue; continue;
readghosttic: readghosttic:
#define follow g->mo->tracer
// Skip normal demo data. // Skip normal demo data.
ziptic = READUINT8(g->p); ziptic = READUINT8(g->p);
xziptic = 0; xziptic = 0;
// Demo ends after ghost data.
if (ziptic == DEMOMARKER)
{
fadeghost:
g->mo->momx = g->mo->momy = g->mo->momz = 0;
g->mo->fuse = TICRATE;
if (follow)
{
follow->fuse = TICRATE;
}
g->done = true;
if (p)
{
p->next = g->next;
}
continue;
}
while (ziptic != DW_END) // Get rid of extradata stuff while (ziptic != DW_END) // Get rid of extradata stuff
{ {
if (ziptic < MAXPLAYERS) if (ziptic < MAXPLAYERS)
@ -1414,9 +1389,11 @@ readghosttic:
// Grab ghost data. // Grab ghost data.
ziptic = READUINT8(g->p); ziptic = READUINT8(g->p);
if (ziptic == DEMOMARKER) // Had to end early for some reason
goto fadeghost;
if (ziptic == 0xFF) if (ziptic == 0xFF)
goto skippedghosttic; // Didn't write ghost info this frame goto skippedghosttic; // Didn't write ghost info this frame
else if (ziptic != 0) if (ziptic != 0)
I_Error("Ghost is not a record attack ghost ZIPTIC"); //@TODO lmao don't blow up like this I_Error("Ghost is not a record attack ghost ZIPTIC"); //@TODO lmao don't blow up like this
ziptic = READUINT8(g->p); ziptic = READUINT8(g->p);
@ -1530,7 +1507,6 @@ readghosttic:
g->mo->renderflags &= ~RF_DONTDRAW; g->mo->renderflags &= ~RF_DONTDRAW;
} }
#define follow g->mo->tracer
if (ziptic & GZT_FOLLOW) if (ziptic & GZT_FOLLOW)
{ // Even more... { // Even more...
UINT8 followtic = READUINT8(g->p); UINT8 followtic = READUINT8(g->p);
@ -1620,28 +1596,6 @@ skippedghosttic:
if (READUINT8(g->p) != 0xFF) // Make sure there isn't other ghost data here. if (READUINT8(g->p) != 0xFF) // Make sure there isn't other ghost data here.
I_Error("Ghost is not a record attack ghost GHOSTEND"); //@TODO lmao don't blow up like this I_Error("Ghost is not a record attack ghost GHOSTEND"); //@TODO lmao don't blow up like this
// Demo ends after ghost data.
if (*g->p == DEMOMARKER)
{
g->mo->momx = g->mo->momy = g->mo->momz = 0;
#if 0 // freeze frame (maybe more useful for time attackers) (2024-03-11: you leave it behind anyway!)
g->mo->colorized = true;
g->mo->fuse = 10*TICRATE;
if (follow)
follow->colorized = true;
#else // dissapearing act
g->mo->fuse = TICRATE;
if (follow)
follow->fuse = TICRATE;
#endif
g->done = true;
if (p)
{
p->next = g->next;
}
continue;
}
// If the timer started, skip ahead until the ghost starts too. // If the timer started, skip ahead until the ghost starts too.
if (starttime <= leveltime && !g->linecrossed && G_TimeAttackStart()) if (starttime <= leveltime && !g->linecrossed && G_TimeAttackStart())
goto readghosttic; goto readghosttic;
@ -1651,203 +1605,6 @@ skippedghosttic:
} }
} }
// Demo rewinding functions
typedef struct rewindinfo_s {
tic_t leveltime;
struct {
boolean ingame;
player_t player;
mobj_t mobj;
} playerinfo[MAXPLAYERS];
struct rewindinfo_s *prev;
} rewindinfo_t;
static tic_t currentrewindnum;
static rewindinfo_t *rewindhead = NULL; // Reverse chronological order
void G_InitDemoRewind(void)
{
CL_ClearRewinds();
while (rewindhead)
{
rewindinfo_t *p = rewindhead->prev;
Z_Free(rewindhead);
rewindhead = p;
}
currentrewindnum = 0;
}
void G_StoreRewindInfo(void)
{
static UINT8 timetolog = 8;
rewindinfo_t *info;
size_t i;
if (timetolog-- > 0)
return;
timetolog = 8;
info = static_cast<rewindinfo_t*>(Z_Calloc(sizeof(rewindinfo_t), PU_STATIC, NULL));
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
{
info->playerinfo[i].ingame = false;
continue;
}
info->playerinfo[i].ingame = true;
memcpy(&info->playerinfo[i].player, &players[i], sizeof(player_t));
if (players[i].mo)
memcpy(&info->playerinfo[i].mobj, players[i].mo, sizeof(mobj_t));
}
info->leveltime = leveltime;
info->prev = rewindhead;
rewindhead = info;
}
void G_PreviewRewind(tic_t previewtime)
{
SINT8 i;
//size_t j;
fixed_t tweenvalue = 0;
rewindinfo_t *info = rewindhead, *next_info = rewindhead;
if (!info)
return;
while (info->leveltime > previewtime && info->prev)
{
next_info = info;
info = info->prev;
}
if (info != next_info)
tweenvalue = FixedDiv(previewtime - info->leveltime, next_info->leveltime - info->leveltime);
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
{
if (info->playerinfo[i].player.mo)
{
//@TODO spawn temp object to act as a player display
}
continue;
}
if (!info->playerinfo[i].ingame || !info->playerinfo[i].player.mo)
{
if (players[i].mo)
players[i].mo->renderflags |= RF_DONTDRAW;
continue;
}
if (!players[i].mo)
continue; //@TODO spawn temp object to act as a player display
players[i].mo->renderflags &= ~RF_DONTDRAW;
P_UnsetThingPosition(players[i].mo);
#define TWEEN(pr) info->playerinfo[i].mobj.pr + FixedMul((INT32) (next_info->playerinfo[i].mobj.pr - info->playerinfo[i].mobj.pr), tweenvalue)
players[i].mo->x = TWEEN(x);
players[i].mo->y = TWEEN(y);
players[i].mo->z = TWEEN(z);
players[i].mo->angle = TWEEN(angle);
#undef TWEEN
P_SetThingPosition(players[i].mo);
players[i].drawangle = info->playerinfo[i].player.drawangle + FixedMul((INT32) (next_info->playerinfo[i].player.drawangle - info->playerinfo[i].player.drawangle), tweenvalue);
players[i].mo->sprite = info->playerinfo[i].mobj.sprite;
players[i].mo->sprite2 = info->playerinfo[i].mobj.sprite2;
players[i].mo->frame = info->playerinfo[i].mobj.frame;
players[i].mo->hitlag = info->playerinfo[i].mobj.hitlag;
players[i].realtime = info->playerinfo[i].player.realtime;
// Genuinely CANNOT be fucked. I can redo lua and I can redo netsaves but I draw the line at this abysmal hack.
/*for (j = 0; j < NUMKARTSTUFF; j++)
players[i].kartstuff[j] = info->playerinfo[i].player.kartstuff[j];*/
}
for (i = splitscreen; i >= 0; i--)
P_ResetCamera(&players[displayplayers[i]], &camera[i]);
}
void G_ConfirmRewind(tic_t rewindtime)
{
SINT8 i;
tic_t j;
boolean oldmenuactive = menuactive, oldsounddisabled = sound_disabled;
INT32 olddp1 = displayplayers[0], olddp2 = displayplayers[1], olddp3 = displayplayers[2], olddp4 = displayplayers[3];
UINT8 oldss = splitscreen;
menuactive = false; // Prevent loops
CV_StealthSetValue(&cv_renderview, 0);
if (rewindtime <= starttime)
{
demo.rewinding = true; // this doesn't APPEAR to cause any misery, and it allows us to prevent running all the wipes again
G_DoPlayDemo(NULL); // Restart the current demo
}
else
{
rewind_t *rewind;
sound_disabled = true; // Prevent sound spam
demo.rewinding = true;
rewind = CL_RewindToTime(rewindtime);
if (rewind)
{
demobuf.p = demobuf.buffer + rewind->demopos;
memcpy(oldcmd, rewind->oldcmd, sizeof (oldcmd));
memcpy(oldghost, rewind->oldghost, sizeof (oldghost));
paused = false;
}
else
{
demo.rewinding = true;
G_DoPlayDemo(NULL); // Restart the current demo
}
}
for (j = 0; j < rewindtime && leveltime < rewindtime; j++)
{
G_Ticker((j % NEWTICRATERATIO) == 0);
}
demo.rewinding = false;
menuactive = oldmenuactive; // Bring the menu back up
sound_disabled = oldsounddisabled; // Re-enable SFX
wipegamestate = gamestate; // No fading back in!
COM_BufInsertText("renderview on\n");
splitscreen = oldss;
displayplayers[0] = olddp1;
displayplayers[1] = olddp2;
displayplayers[2] = olddp3;
displayplayers[3] = olddp4;
R_ExecuteSetViewSize();
G_ResetViews();
for (i = splitscreen; i >= 0; i--)
P_ResetCamera(&players[displayplayers[i]], &camera[i]);
}
// //
// G_RecordDemo // G_RecordDemo
// //
@ -1885,7 +1642,7 @@ void G_RecordDemo(const char *name)
functions will check if they overran the buffer, but it functions will check if they overran the buffer, but it
should be safe enough because they'll think there's less should be safe enough because they'll think there's less
memory than there actually is and stop early. */ memory than there actually is and stop early. */
const size_t deadspace = 1024; const size_t deadspace = 2048;
I_Assert(demobuf.size > deadspace); I_Assert(demobuf.size > deadspace);
demobuf.size -= deadspace; demobuf.size -= deadspace;
demobuf.end -= deadspace; demobuf.end -= deadspace;
@ -2999,8 +2756,6 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
boolean skiperrors = true; boolean skiperrors = true;
#endif #endif
G_InitDemoRewind();
gtname[MAXGAMETYPELENGTH-1] = '\0'; gtname[MAXGAMETYPELENGTH-1] = '\0';
if (deflumpnum != LUMPERROR) if (deflumpnum != LUMPERROR)
@ -3363,22 +3118,6 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
// Load "mapmusrng" used for altmusic selection // Load "mapmusrng" used for altmusic selection
mapmusrng = READUINT8(demobuf.p); mapmusrng = READUINT8(demobuf.p);
// Sigh ... it's an empty demo.
if (*demobuf.p == DEMOMARKER)
{
snprintf(msg, 1024, M_GetText("%s contains no data to be played.\n"), pdemoname);
CONS_Alert(CONS_ERROR, "%s", msg);
M_StartMessage("Demo Playback", msg, NULL, MM_NOTHING, NULL, "Return to Menu");
Z_Free(demo.skinlist);
demo.skinlist = NULL;
Z_Free(pdemoname);
Z_Free(demobuf.buffer);
demo.playback = false;
return;
}
Z_Free(pdemoname);
memset(&oldcmd,0,sizeof(oldcmd)); memset(&oldcmd,0,sizeof(oldcmd));
memset(&oldghost,0,sizeof(oldghost)); memset(&oldghost,0,sizeof(oldghost));
memset(&ghostext,0,sizeof(ghostext)); memset(&ghostext,0,sizeof(ghostext));
@ -3607,9 +3346,26 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
players[p].lastfakeskin = lastfakeskin[p]; players[p].lastfakeskin = lastfakeskin[p];
} }
// Sigh ... it's an empty demo.
if (*demobuf.p == DEMOMARKER)
{
snprintf(msg, 1024, M_GetText("%s contains no data to be played.\n"), pdemoname);
CONS_Alert(CONS_ERROR, "%s", msg);
M_StartMessage("Demo Playback", msg, NULL, MM_NOTHING, NULL, "Return to Menu");
Z_Free(demo.skinlist);
demo.skinlist = NULL;
Z_Free(pdemoname);
Z_Free(demobuf.buffer);
demo.playback = false;
return;
}
Z_Free(pdemoname);
demo.deferstart = true; demo.deferstart = true;
CV_StealthSetValue(&cv_playbackspeed, 1); if (demo.simplerewind == DEMO_REWIND_OFF)
CV_StealthSetValue(&cv_playbackspeed, 1);
} }
void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) void G_AddGhost(savebuffer_t *buffer, const char *defdemoname)
@ -3756,14 +3512,6 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname)
p++; // mapmusrng p++; // mapmusrng
if (*p == DEMOMARKER)
{
CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Replay is empty.\n"), defdemoname);
Z_Free(skinlist);
P_SaveBufferFree(buffer);
return;
}
p++; // player number - doesn't really need to be checked, TODO maybe support adding multiple players' ghosts at once p++; // player number - doesn't really need to be checked, TODO maybe support adding multiple players' ghosts at once
// any invalidating flags? // any invalidating flags?
@ -3811,6 +3559,14 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname)
return; return;
} }
if (*p == DEMOMARKER)
{
CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Replay is empty.\n"), defdemoname);
Z_Free(skinlist);
P_SaveBufferFree(buffer);
return;
}
gh = static_cast<demoghost*>(Z_Calloc(sizeof(demoghost), PU_LEVEL, NULL)); gh = static_cast<demoghost*>(Z_Calloc(sizeof(demoghost), PU_LEVEL, NULL));
gh->sizes = ghostsizes; gh->sizes = ghostsizes;
@ -4173,7 +3929,7 @@ boolean G_CheckDemoStatus(void)
// Keep the demo open and don't boot to intermission // Keep the demo open and don't boot to intermission
// YET, pause demo playback. // YET, pause demo playback.
if (!demo.waitingfortally && modeattacking && exitcountdown) if (!demo.waitingfortally && modeattacking && exitcountdown)
demo.waitingfortally = true; ;
else if (!demo.attract) else if (!demo.attract)
G_FinishExitLevel(); G_FinishExitLevel();
else else
@ -4191,6 +3947,8 @@ boolean G_CheckDemoStatus(void)
D_SetDeferredStartTitle(true); D_SetDeferredStartTitle(true);
} }
demo.waitingfortally = true; // if we've returned early for some reason...
return true; return true;
} }
@ -4235,6 +3993,13 @@ void G_SaveDemo(void)
if (currentMenu == &TitleEntryDef) if (currentMenu == &TitleEntryDef)
M_ClearMenus(true); M_ClearMenus(true);
if (!leveltime)
{
// Why would you save if nothing has been recorded
G_ResetDemoRecording();
return;
}
// Ensure extrainfo pointer is always available, even if no info is present. // Ensure extrainfo pointer is always available, even if no info is present.
if (demoinfo_p && *(UINT32 *)demoinfo_p == 0) if (demoinfo_p && *(UINT32 *)demoinfo_p == 0)
{ {

View file

@ -84,7 +84,7 @@ struct demovars_s {
boolean recording, playback, timing; boolean recording, playback, timing;
UINT16 version; // Current file format of the demo being played UINT16 version; // Current file format of the demo being played
UINT8 attract; // Attract demo can be cancelled by any key UINT8 attract; // Attract demo can be cancelled by any key
boolean rewinding; // Rewind in progress UINT8 simplerewind;
boolean loadfiles, ignorefiles; // Demo file loading options boolean loadfiles, ignorefiles; // Demo file loading options
boolean quitafterplaying; // quit after playing a demo from cmdline boolean quitafterplaying; // quit after playing a demo from cmdline
@ -172,6 +172,8 @@ extern UINT8 demo_writerng;
boolean G_CompatLevel(UINT16 level); boolean G_CompatLevel(UINT16 level);
// Record/playback tics // Record/playback tics
boolean G_ConsiderEndingDemoRead(void);
boolean G_ConsiderEndingDemoWrite(void);
void G_ReadDemoExtraData(void); void G_ReadDemoExtraData(void);
void G_WriteDemoExtraData(void); void G_WriteDemoExtraData(void);
void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum); void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum);
@ -186,11 +188,6 @@ void G_ConsAllGhostTics(void);
void G_ConsGhostTic(INT32 playernum); void G_ConsGhostTic(INT32 playernum);
void G_GhostTicker(void); void G_GhostTicker(void);
void G_InitDemoRewind(void);
void G_StoreRewindInfo(void);
void G_PreviewRewind(tic_t previewtime);
void G_ConfirmRewind(tic_t rewindtime);
struct DemoBufferSizes struct DemoBufferSizes
{ {
size_t player_name; size_t player_name;
@ -250,6 +247,13 @@ typedef enum
DEMO_ATTRACT_CREDITS DEMO_ATTRACT_CREDITS
} demoAttractMode_t; } demoAttractMode_t;
typedef enum
{
DEMO_REWIND_OFF = 0,
DEMO_REWIND_RESUME,
DEMO_REWIND_PAUSE
} demoRewindMode_t;
void G_SyncDemoParty(INT32 rem, INT32 newsplitscreen); void G_SyncDemoParty(INT32 rem, INT32 newsplitscreen);
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -1245,26 +1245,36 @@ void G_StartTitleCard(void)
// prepare status bar // prepare status bar
ST_startTitleCard(); // <-- always must be called to init some variables ST_startTitleCard(); // <-- always must be called to init some variables
// The title card has been disabled for this map. if (demo.simplerewind)
// Oh well.
if (demo.rewinding || !G_IsTitleCardAvailable())
{
WipeStageTitle = false;
return; return;
sfxenum_t kstart = 0;
if (K_CheckBossIntro() == true)
{
kstart = sfx_ssa021;
} }
else if (encoremode)
{
kstart = sfx_ruby2;
}
if (kstart)
{
// Play the guaranteed alt sounds
S_StartSound(NULL, kstart);
}
if (!G_IsTitleCardAvailable())
return;
// start the title card // start the title card
WipeStageTitle = (gamestate == GS_LEVEL); WipeStageTitle = (gamestate == GS_LEVEL);
// play the sound if (WipeStageTitle && !kstart)
if (WipeStageTitle)
{ {
sfxenum_t kstart = sfx_kstart; // Play the standard titlecard sound
if (K_CheckBossIntro() == true) S_StartSound(NULL, sfx_kstart);
kstart = sfx_ssa021;
else if (encoremode == true)
kstart = sfx_ruby2;
S_StartSound(NULL, kstart);
} }
} }
@ -1439,13 +1449,7 @@ boolean G_Responder(event_t *ev)
{ {
paused = !paused; paused = !paused;
if (demo.rewinding) if (paused)
{
G_ConfirmRewind(leveltime);
paused = true;
S_PauseAudio();
}
else if (paused)
S_PauseAudio(); S_PauseAudio();
else else
S_ResumeAudio(); S_ResumeAudio();
@ -2150,6 +2154,14 @@ void G_Ticker(boolean run)
if (g_fast_forward == 0) if (g_fast_forward == 0)
{ {
// Not "rewinding" anymore.
if (demo.simplerewind == DEMO_REWIND_PAUSE)
{
paused = true;
S_PauseAudio();
}
demo.simplerewind = DEMO_REWIND_OFF;
// Next fast-forward is unlimited. // Next fast-forward is unlimited.
g_fast_forward_clock_stop = INFTICS; g_fast_forward_clock_stop = INFTICS;
} }
@ -4094,6 +4106,7 @@ doremove:
// Next map apparatus // Next map apparatus
struct roundqueue roundqueue; struct roundqueue roundqueue;
struct menuqueue menuqueue;
void G_MapSlipIntoRoundQueue(UINT8 position, UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted) void G_MapSlipIntoRoundQueue(UINT8 position, UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted)
{ {
@ -5377,7 +5390,7 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr
S_ResumeAudio(); S_ResumeAudio();
} }
prevencoremode = ((!Playing()) ? false : encoremode); prevencoremode = encoremode;
encoremode = pencoremode; encoremode = pencoremode;
legitimateexit = false; // SRB2Kart legitimateexit = false; // SRB2Kart
@ -5829,7 +5842,7 @@ boolean G_GetExitGameFlag(void)
// Same deal with retrying. // Same deal with retrying.
void G_SetRetryFlag(void) void G_SetRetryFlag(void)
{ {
if (retrying == false) if (retrying == false && grandprixinfo.gp)
{ {
grandprixinfo.rank.continuesUsed++; grandprixinfo.rank.continuesUsed++;
} }

View file

@ -75,6 +75,15 @@ extern struct roundqueue
roundentry_t entries[ROUNDQUEUE_MAX]; // Entries in the round queue roundentry_t entries[ROUNDQUEUE_MAX]; // Entries in the round queue
} roundqueue; } roundqueue;
extern struct menuqueue
{
// Degenerate version of roundqueue exclusively for menu use.
UINT8 size;
UINT8 sending;
UINT8 anchor;
roundentry_t entries[ROUNDQUEUE_MAX];
} menuqueue;
void G_MapSlipIntoRoundQueue(UINT8 position, UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted); void G_MapSlipIntoRoundQueue(UINT8 position, UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted);
void G_MapIntoRoundQueue(UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted); void G_MapIntoRoundQueue(UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted);
void G_GPCupIntoRoundQueue(cupheader_t *cup, UINT8 setgametype, boolean setencore); void G_GPCupIntoRoundQueue(cupheader_t *cup, UINT8 setgametype, boolean setencore);

View file

@ -1536,6 +1536,46 @@ void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, p
); );
} }
void K_DrawMapAsFace(INT32 x, INT32 y, UINT32 flags, UINT16 map, const UINT8 *colormap)
{
const fixed_t iconHeight = (14 << FRACBITS);
const fixed_t iconWidth = (iconHeight * 320) / 200;
INT32 unit = 1;
fixed_t mul = FRACUNIT;
if (flags & V_NOSCALESTART)
{
unit = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
mul = 1;
}
V_DrawFill(
x,
y,
16 * unit,
16 * unit,
(flags & ~V_FLIP)
);
V_SetClipRect(
(x + unit) * mul,
(y + unit) * mul,
(14 * unit) * mul,
(14 * unit) * mul,
(flags & ~V_FLIP)
);
K_DrawMapThumbnail(
((x + unit) * FRACUNIT) - (iconWidth - iconHeight)/2,
((y + unit) * FRACUNIT),
iconWidth,
flags,
map,
colormap
);
V_ClearClipRect();
}
// see also K_DrawNameTagItemSpy // see also K_DrawNameTagItemSpy
static void K_drawKartItem(void) static void K_drawKartItem(void)
{ {

View file

@ -60,6 +60,7 @@ void K_drawKart4PTimestamp(void);
void K_drawEmeraldWin(boolean overlay); void K_drawEmeraldWin(boolean overlay);
void K_DrawMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, UINT16 map, const UINT8 *colormap); void K_DrawMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, UINT16 map, const UINT8 *colormap);
void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, patch_t *patch, const UINT8 *colormap); void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, patch_t *patch, const UINT8 *colormap);
void K_DrawMapAsFace(INT32 x, INT32 y, UINT32 flags, UINT16 map, const UINT8 *colormap);
void K_drawTargetHUD(const vector3_t *origin, player_t *player); void K_drawTargetHUD(const vector3_t *origin, player_t *player);
void K_drawButton(fixed_t x, fixed_t y, INT32 flags, patch_t *button[2], boolean pressed); void K_drawButton(fixed_t x, fixed_t y, INT32 flags, patch_t *button[2], boolean pressed);
void K_drawButtonAnim(INT32 x, INT32 y, INT32 flags, patch_t *button[2], tic_t animtic); void K_drawButtonAnim(INT32 x, INT32 y, INT32 flags, patch_t *button[2], tic_t animtic);

View file

@ -593,10 +593,10 @@ extern menu_t PAUSE_PlaybackMenuDef;
typedef enum typedef enum
{ {
playback_hide, playback_hide,
playback_restart,
playback_rewind, playback_rewind,
playback_pause, playback_pause,
playback_fastforward, playback_fastforward,
playback_backframe,
playback_resume, playback_resume,
playback_advanceframe, playback_advanceframe,
playback_viewcount, playback_viewcount,
@ -923,6 +923,7 @@ typedef struct levellist_s {
UINT8 guessgt; UINT8 guessgt;
levelsearch_t levelsearch; levelsearch_t levelsearch;
boolean netgame; // Start the game in an actual server boolean netgame; // Start the game in an actual server
boolean canqueue;
menu_t *backMenu; menu_t *backMenu;
} levellist_t; } levellist_t;
@ -944,6 +945,8 @@ void M_CupSelectTick(void);
void M_LevelSelectHandler(INT32 choice); void M_LevelSelectHandler(INT32 choice);
void M_LevelSelectTick(void); void M_LevelSelectTick(void);
INT16 M_LevelFromScrolledList(INT16 add);
void M_MenuToLevelPreamble(UINT8 ssplayers, boolean nowipe);
void M_LevelSelected(INT16 add, boolean menuupdate); void M_LevelSelected(INT16 add, boolean menuupdate);
boolean M_LevelSelectCupSwitch(boolean next, boolean skipones); boolean M_LevelSelectCupSwitch(boolean next, boolean skipones);

View file

@ -755,33 +755,40 @@ static void M_DrawMenuTyping(void)
} }
// Largely replaced by boxed drawing mode in K_DrawGameControl and rich text static void M_DrawPauseRoundQueue(INT16 offset, boolean canqueue)
/*
static void M_DrawMediocreKeyboardKey(const char *text, INT32 *workx, INT32 worky, boolean push, boolean rightaligned)
{ {
INT32 buttonwidth = V_StringWidth(text, 0) + 2; y_data_t standings;
memset(&standings, 0, sizeof (standings));
if (rightaligned) if (gamestate == GS_MENU)
{ {
(*workx) -= buttonwidth; standings.mainplayer = MAXPLAYERS;
}
if (push)
{
worky += 2;
} }
else else
{ {
V_DrawFill((*workx)-1, worky+10, buttonwidth, 2, 24); standings.mainplayer = (demo.playback ? displayplayers[0] : consoleplayer);
} }
V_DrawFill((*workx)-1, worky, buttonwidth, 10, 16); // See also G_GetNextMap, Y_CalculateMatchData
V_DrawString( if (
(*workx), worky + 1, canqueue == false
0, text && grandprixinfo.gp == true
); && netgame == false // TODO netgame Special Mode support
&& grandprixinfo.gamespeed >= KARTSPEED_NORMAL
&& roundqueue.size > 1
&& roundqueue.entries[roundqueue.size - 1].rankrestricted == true
&& (
gamedata->everseenspecial == true
|| roundqueue.position == roundqueue.size
)
)
{
// Additional cases in which it should always be shown.
standings.showrank = true;
}
Y_RoundQueueDrawer(&standings, offset, false, false, canqueue);
} }
*/
// Draw the message popup submenu // Draw the message popup submenu
void M_DrawMenuMessage(void) void M_DrawMenuMessage(void)
@ -991,6 +998,16 @@ void M_Drawer(void)
// Draw message overlay when needed // Draw message overlay when needed
M_DrawMenuMessage(); M_DrawMenuMessage();
if (
(
currentMenu == &PLAY_LevelSelectDef
|| currentMenu == &PLAY_CupSelectDef
) && levellist.canqueue
)
{
M_DrawPauseRoundQueue(0, true);
}
} }
if (menuwipe) if (menuwipe)
@ -6288,30 +6305,11 @@ void M_DrawPause(void)
V_DrawCenteredLSTitleLowString(220 + offset*2, 103, mainflags, word2); V_DrawCenteredLSTitleLowString(220 + offset*2, 103, mainflags, word2);
} }
const boolean rulescheck = (K_CanChangeRules(false) && (server || IsPlayerAdmin(consoleplayer)));
boolean drawqueue = (rulescheck && (menuqueue.size > 0));
if (gamestate != GS_INTERMISSION && roundqueue.size > 0) if (gamestate != GS_INTERMISSION && roundqueue.size > 0)
{ {
y_data_t standings;
memset(&standings, 0, sizeof (standings));
standings.mainplayer = (demo.playback ? displayplayers[0] : consoleplayer);
// See also G_GetNextMap, Y_CalculateMatchData
if (
grandprixinfo.gp == true
&& netgame == false // TODO netgame Special Mode support
&& grandprixinfo.gamespeed >= KARTSPEED_NORMAL
&& roundqueue.size > 1
&& roundqueue.entries[roundqueue.size - 1].rankrestricted == true
&& (
gamedata->everseenspecial == true
|| roundqueue.position == roundqueue.size
)
)
{
// Additional cases in which it should always be shown.
standings.showrank = true;
}
if (roundqueue.position > 0 && roundqueue.position <= roundqueue.size) if (roundqueue.position > 0 && roundqueue.position <= roundqueue.size)
{ {
patch_t *smallroundpatch = ST_getRoundPicture(true); patch_t *smallroundpatch = ST_getRoundPicture(true);
@ -6328,7 +6326,7 @@ void M_DrawPause(void)
V_DrawCenteredMenuString(24, 167 + offset/2, V_YELLOWMAP, M_GetGameplayMode()); V_DrawCenteredMenuString(24, 167 + offset/2, V_YELLOWMAP, M_GetGameplayMode());
Y_RoundQueueDrawer(&standings, offset/2, false, false); drawqueue = true;
} }
else if (gametype == GT_TUTORIAL) else if (gametype == GT_TUTORIAL)
{ {
@ -6347,6 +6345,11 @@ void M_DrawPause(void)
{ {
V_DrawMenuString(4, 188 + offset/2, V_YELLOWMAP, M_GetGameplayMode()); V_DrawMenuString(4, 188 + offset/2, V_YELLOWMAP, M_GetGameplayMode());
} }
if (drawqueue)
{
M_DrawPauseRoundQueue(offset/2, rulescheck);
}
} }
void M_DrawKickHandler(void) void M_DrawKickHandler(void)
@ -6485,7 +6488,7 @@ void M_DrawPlaybackMenu(void)
else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR) else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR)
icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE); icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE);
if ((i == playback_fastforward && cv_playbackspeed.value > 1) || (i == playback_rewind && demo.rewinding)) if ((i == playback_fastforward && cv_playbackspeed.value > 1))
V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].mvar1, currentMenu->y, V_SNAPTOTOP, icon, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JAWZ, GTC_MENUCACHE)); V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].mvar1, currentMenu->y, V_SNAPTOTOP, icon, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JAWZ, GTC_MENUCACHE));
else else
V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].mvar1, currentMenu->y, V_SNAPTOTOP, icon, (i == itemOn) ? activemap : inactivemap); V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].mvar1, currentMenu->y, V_SNAPTOTOP, icon, (i == itemOn) ? activemap : inactivemap);

View file

@ -466,7 +466,7 @@ boolean M_Responder(event_t *ev)
if (Playing() && !demo.playback) if (Playing() && !demo.playback)
{ {
// Quick Retry (Y in modeattacking) // Quick Retry (Y in modeattacking)
if (modeattacking && G_PlayerInputDown(0, gc_y, splitscreen + 1) == true) if (modeattacking && G_PlayerInputDown(0, gc_respawn, splitscreen + 1) == true)
{ {
M_TryAgain(0); M_TryAgain(0);
return true; return true;

View file

@ -700,35 +700,13 @@ static void Y_DrawVoteThumbnail(fixed_t center_x, fixed_t center_y, fixed_t widt
} }
else if (vote.stage_striking == false) // Angry map else if (vote.stage_striking == false) // Angry map
{ {
const fixed_t iconHeight = (14 << FRACBITS); K_DrawMapAsFace(
const fixed_t iconWidth = (iconHeight * 320) / 200;
V_DrawFill(
fx + fw - whiteSq + dupx, fx + fw - whiteSq + dupx,
fy + fh - whiteSq + dupy, fy + fh - whiteSq + dupy,
whiteSq,
whiteSq,
0|flags|V_NOSCALESTART
);
V_SetClipRect(
fx + fw - whiteSq + (2 * dupx),
fy + fh - whiteSq + (2 * dupy),
whiteSq - (2 * dupx),
whiteSq - (2 * dupy),
flags|V_NOSCALESTART
);
K_DrawMapThumbnail(
((fx + fw - whiteSq + (2 * dupx)) * FRACUNIT) - (iconWidth - iconHeight),
(fy + fh - whiteSq + (2 * dupy)) * FRACUNIT,
iconWidth,
flags | V_NOSCALESTART | ((encore == true) ? V_FLIP : 0), flags | V_NOSCALESTART | ((encore == true) ? V_FLIP : 0),
g_voteLevels[v][0], g_voteLevels[v][0],
NULL NULL
); );
V_ClearClipRect();
} }
} }
} }

View file

@ -1272,6 +1272,7 @@ void M_GonerTutorial(INT32 choice)
// Please also see M_LevelSelectInit as called in extras-1.c // Please also see M_LevelSelectInit as called in extras-1.c
levellist.netgame = false; levellist.netgame = false;
levellist.canqueue = false;
levellist.levelsearch.checklocked = true; levellist.levelsearch.checklocked = true;
levellist.levelsearch.grandprix = false; levellist.levelsearch.grandprix = false;
levellist.levelsearch.timeattack = false; levellist.levelsearch.timeattack = false;

View file

@ -614,34 +614,19 @@ void M_StartTimeAttack(INT32 choice)
{ {
modeattacking |= ATTACKING_SPB; modeattacking |= ATTACKING_SPB;
} }
if (gamestate == GS_MENU)
{
encoremode = true; // guarantees short wipe
}
modeprefix = "spb-"; modeprefix = "spb-";
} }
// DON'T SOFTLOCK // DON'T SOFTLOCK
CON_ToggleOff(); CON_ToggleOff();
// Still need to reset devmode M_MenuToLevelPreamble(0, (gamestate != GS_MENU || cv_dummyspbattack.value == 1));
cht_debug = 0;
if (demo.playback)
G_StopDemo();
splitscreen = 0;
SplitScreen_OnChange();
S_StartSound(NULL, sfx_s3k63);
paused = false;
S_StopMusicCredit();
// Early fadeout to let the sound finish playing
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
SV_StartSinglePlayerServer(levellist.newgametype, false);
gpath = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s", gpath = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s",
srb2home, timeattackfolder); srb2home, timeattackfolder);
@ -659,8 +644,17 @@ void M_StartTimeAttack(INT32 choice)
restoreMenu = &PLAY_TimeAttackDef; restoreMenu = &PLAY_TimeAttackDef;
D_MapChange(
levellist.choosemap+1,
levellist.newgametype,
(cv_dummyspbattack.value == 1),
true,
1,
false,
false
);
M_ClearMenus(true); M_ClearMenus(true);
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_dummyspbattack.value == 1), 1, 1, false, false);
G_UpdateTimeStickerMedals(levellist.choosemap, true); G_UpdateTimeStickerMedals(levellist.choosemap, true);
} }

View file

@ -172,6 +172,7 @@ void M_MPSetupNetgameMapSelect(INT32 choice)
// Yep, we'll be starting a netgame. // Yep, we'll be starting a netgame.
levellist.netgame = true; levellist.netgame = true;
levellist.canqueue = true;
// Make sure we reset those // Make sure we reset those
levellist.levelsearch.timeattack = false; levellist.levelsearch.timeattack = false;
levellist.levelsearch.checklocked = true; levellist.levelsearch.checklocked = true;

View file

@ -57,26 +57,7 @@ static void M_StartCup(UINT8 entry)
entry = UINT8_MAX; entry = UINT8_MAX;
} }
S_StartSound(NULL, sfx_s3k63); M_MenuToLevelPreamble(ssplayers, false);
paused = false;
S_StopMusicCredit();
// Early fadeout to let the sound finish playing
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
if (cv_maxconnections.value < ssplayers+1)
CV_SetValue(&cv_maxconnections, ssplayers+1);
if (splitscreen != ssplayers)
{
splitscreen = ssplayers;
SplitScreen_OnChange();
}
if (entry == UINT8_MAX) if (entry == UINT8_MAX)
{ {
@ -135,10 +116,6 @@ static void M_StartCup(UINT8 entry)
} }
} }
paused = false;
SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame);
M_ClearMenus(true); M_ClearMenus(true);
restoreMenu = &PLAY_CupSelectDef; restoreMenu = &PLAY_CupSelectDef;

View file

@ -21,6 +21,7 @@
#include "../../v_video.h" #include "../../v_video.h"
#include "../../g_game.h" // G_GetBackupCupData #include "../../g_game.h" // G_GetBackupCupData
#include "../../p_saveg.h" // cupsavedata #include "../../p_saveg.h" // cupsavedata
#include "../../d_netcmd.h" // Handle_MapQueueSend
cupheader_t dummy_lostandfound; cupheader_t dummy_lostandfound;
@ -657,14 +658,22 @@ void M_LevelSelectInit(INT32 choice)
case 0: case 0:
levellist.levelsearch.grandprix = false; levellist.levelsearch.grandprix = false;
levellist.levelsearch.timeattack = false; levellist.levelsearch.timeattack = false;
levellist.canqueue = true;
CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string);
CV_StealthSet(&cv_kartencore, (cv_dummygpencore.value == 1) ? "On" : "Auto");
CV_StealthSet(&cv_kartspeed, (cv_dummykartspeed.value == KARTSPEED_NORMAL) ? "Auto Gear" : cv_dummykartspeed.string);
break; break;
case 1: case 1:
levellist.levelsearch.grandprix = false; levellist.levelsearch.grandprix = false;
levellist.levelsearch.timeattack = true; levellist.levelsearch.timeattack = true;
levellist.canqueue = false;
break; break;
case 2: case 2:
levellist.levelsearch.grandprix = true; levellist.levelsearch.grandprix = true;
levellist.levelsearch.timeattack = false; levellist.levelsearch.timeattack = false;
levellist.canqueue = false;
break; break;
default: default:
CONS_Alert(CONS_WARNING, "Bad level select init\n"); CONS_Alert(CONS_WARNING, "Bad level select init\n");
@ -676,10 +685,14 @@ void M_LevelSelectInit(INT32 choice)
gt = menugametype; gt = menugametype;
} }
if (levellist.levelsearch.timeattack && gt == GT_RACE && skins[R_SkinAvailableEx(cv_skin[0].string, false)].flags & SF_HIVOLT) if (levellist.levelsearch.timeattack && gt == GT_RACE)
{ {
M_StartMessage("A long-forgotten power...", "You are using a \x82prototype engine\x80.\nRecords will not be saved.", NULL, MM_NOTHING, NULL, NULL); const INT32 skinid = R_SkinAvailableEx(cv_skin[0].string, false);
S_StartSound(NULL, sfx_s3k81); if (skinid >= 0 && (skins[skinid].flags & SF_HIVOLT))
{
M_StartMessage("A long-forgotten power...", "You are using a \x82prototype engine\x80.\nRecords will not be saved.", NULL, MM_NOTHING, NULL, NULL);
S_StartSound(NULL, sfx_s3k81);
}
} }
if (!M_LevelListFromGametype(gt)) if (!M_LevelListFromGametype(gt))
@ -689,7 +702,41 @@ void M_LevelSelectInit(INT32 choice)
} }
} }
void M_LevelSelected(INT16 add, boolean menuupdate) void M_MenuToLevelPreamble(UINT8 ssplayers, boolean nowipe)
{
cht_debug = 0;
if (demo.playback)
G_StopDemo();
if (cv_maxconnections.value < ssplayers+1)
CV_SetValue(&cv_maxconnections, ssplayers+1);
if (splitscreen != ssplayers)
{
splitscreen = ssplayers;
SplitScreen_OnChange();
}
paused = false;
S_StopMusicCredit();
if (!nowipe)
{
S_StartSound(NULL, sfx_s3k63);
// Early fadeout to let the sound finish playing
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
}
SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame);
}
INT16 M_LevelFromScrolledList(INT16 add)
{ {
UINT8 i = 0; UINT8 i = 0;
INT16 map = M_GetFirstLevelInList(&i, &levellist.levelsearch); INT16 map = M_GetFirstLevelInList(&i, &levellist.levelsearch);
@ -706,6 +753,13 @@ void M_LevelSelected(INT16 add, boolean menuupdate)
add--; add--;
} }
return map;
}
void M_LevelSelected(INT16 add, boolean menuupdate)
{
INT16 map = M_LevelFromScrolledList(add);
if (map >= nummapheaders) if (map >= nummapheaders)
{ {
// This shouldn't happen // This shouldn't happen
@ -733,75 +787,116 @@ void M_LevelSelected(INT16 add, boolean menuupdate)
{ {
if (gamestate == GS_MENU) if (gamestate == GS_MENU)
{ {
UINT8 ssplayers = levellist.levelsearch.tutorial ? 0 : cv_splitplayers.value-1;
netgame = false;
multiplayer = true; multiplayer = true;
strlcpy(connectedservername, cv_servername.string, MAXSERVERNAME); M_MenuToLevelPreamble(
(levellist.levelsearch.tutorial
? 0
: cv_splitplayers.value-1
),
(
(cv_kartencore.value == 1)
&& (gametypes[levellist.newgametype]->rules & GTR_ENCORE)
)
);
// Still need to reset devmode restoreMenu = (levellist.netgame)
cht_debug = 0; ? &PLAY_MP_OptSelectDef
: currentMenu;
if (demo.playback)
G_StopDemo();
/*if (levellist.choosemap == 0)
levellist.choosemap = G_RandMap(G_TOLFlag(levellist.newgametype), -1, 0, 0, false, NULL);*/
if (cv_maxconnections.value < ssplayers+1)
CV_SetValue(&cv_maxconnections, ssplayers+1);
if (splitscreen != ssplayers)
{
splitscreen = ssplayers;
SplitScreen_OnChange();
}
S_StartSound(NULL, sfx_s3k63);
paused = false;
S_StopMusicCredit();
// Early fadeout to let the sound finish playing
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame);
if (!levellist.netgame)
CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string);
CV_StealthSet(&cv_kartencore, (cv_dummygpencore.value == 1) ? "On" : "Auto");
CV_StealthSet(&cv_kartspeed, (cv_dummykartspeed.value == KARTSPEED_NORMAL) ? "Auto Gear" : cv_dummykartspeed.string);
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false);
if (levellist.netgame == true)
{
restoreMenu = &PLAY_MP_OptSelectDef;
}
else /*if (!M_GameTrulyStarted() ||
levellist.levelsearch.tutorial)*/
{
restoreMenu = currentMenu;
}
restorelevellist = levellist; restorelevellist = levellist;
} }
else
{ D_MapChange(
// directly do the map change levellist.choosemap+1,
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); levellist.newgametype,
} (cv_kartencore.value == 1),
true,
1,
false,
false
);
M_ClearMenus(true); M_ClearMenus(true);
} }
} }
static void M_MenuQueueStopSend(INT32 ch)
{
(void)ch;
memset(&menuqueue, 0, sizeof(struct menuqueue));
}
static void M_MenuQueueSelectedLocal(void)
{
UINT8 i = 0;
for (; i < menuqueue.size; i++)
{
G_MapIntoRoundQueue(
menuqueue.entries[i].mapnum,
menuqueue.entries[i].gametype,
menuqueue.entries[i].encore,
false
);
}
roundqueue.netcommunicate = true;
if (gamestate == GS_MENU)
{
levellist.choosemap = roundqueue.entries[0].mapnum;
multiplayer = true;
M_MenuToLevelPreamble(
cv_splitplayers.value-1,
(
roundqueue.entries[0].encore
&& (gametypes[roundqueue.entries[0].gametype]->rules & GTR_ENCORE)
)
);
restoreMenu = (levellist.netgame)
? &PLAY_MP_OptSelectDef
: currentMenu;
restorelevellist = levellist;
roundqueue.position = roundqueue.roundnum = 1;
D_MapChange(
roundqueue.entries[0].mapnum + 1,
roundqueue.entries[0].gametype,
roundqueue.entries[0].encore,
true,
1,
false,
roundqueue.entries[0].rankrestricted
);
M_MenuQueueStopSend(MA_NONE);
M_ClearMenus(true);
}
else
{
if (gamestate == GS_LEVEL && roundqueue.position == 0)
{
menuqueue.sending = UINT8_MAX;
}
else
{
S_StartSound(NULL, sfx_chchng);
M_MenuQueueStopSend(MA_NONE);
M_ClearMenus(true);
}
}
}
boolean M_LevelSelectCupSwitch(boolean next, boolean skipones) boolean M_LevelSelectCupSwitch(boolean next, boolean skipones)
{ {
levelsearch_t templevelsearch = levellist.levelsearch; levelsearch_t templevelsearch = levellist.levelsearch;
@ -883,6 +978,44 @@ boolean M_LevelSelectCupSwitch(boolean next, boolean skipones)
} }
} }
static void M_MenuQueueResponse(INT32 ch)
{
M_ClearMenus(false);
if (ch != MA_YES)
return;
if (!(server || (IsPlayerAdmin(consoleplayer))))
return;
if (!(gamestate == GS_LEVEL && roundqueue.position == 0))
return;
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
static void M_ClearQueueResponse(INT32 ch)
{
if (ch != MA_YES)
return;
if (!(server || (IsPlayerAdmin(consoleplayer))))
return;
S_StartSound(NULL, sfx_slip);
if (netgame)
{
if (roundqueue.size)
{
Handle_MapQueueSend(0, ROUNDQUEUE_CMD_CLEAR, false);
}
return;
}
memset(&roundqueue, 0, sizeof(struct roundqueue));
}
void M_LevelSelectHandler(INT32 choice) void M_LevelSelectHandler(INT32 choice)
{ {
const UINT8 pid = 0; const UINT8 pid = 0;
@ -894,6 +1027,11 @@ void M_LevelSelectHandler(INT32 choice)
return; return;
} }
if (menuqueue.sending)
{
return;
}
if (menucmd[pid].dpad_ud > 0) if (menucmd[pid].dpad_ud > 0)
{ {
levellist.cursor++; levellist.cursor++;
@ -926,10 +1064,96 @@ void M_LevelSelectHandler(INT32 choice)
M_LevelSelectScrollDest(); M_LevelSelectScrollDest();
if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/) if (M_MenuConfirmPressed(pid))
{ {
// Starting immediately OR importing queue
M_SetMenuDelay(pid); M_SetMenuDelay(pid);
M_LevelSelected(levellist.cursor, true);
while ((menuqueue.size + roundqueue.size) > ROUNDQUEUE_MAX)
menuqueue.size--;
if (!levellist.canqueue || !menuqueue.size)
{
M_LevelSelected(levellist.cursor, true);
}
else if (netgame)
{
menuqueue.anchor = roundqueue.size;
menuqueue.sending = 1;
M_StartMessage("Queueing Rounds",
va(M_GetText(
"Attempting to send %d Round%s...\n"
"\n"
"If this is taking longer than you\n"
"expect, exit out of this message.\n"
), menuqueue.size, (menuqueue.size == 1 ? "" : "s")
), &M_MenuQueueStopSend, MM_NOTHING,
NULL,
"This is taking too long..."
);
}
else
{
M_MenuQueueSelectedLocal();
}
}
else if (levellist.canqueue && M_MenuButtonPressed(pid, MBT_Z))
{
// Adding to queue
INT16 map = NEXTMAP_INVALID;
M_SetMenuDelay(pid);
if ((roundqueue.size + menuqueue.size) < ROUNDQUEUE_MAX)
{
map = M_LevelFromScrolledList(levellist.cursor);
}
if (map < nummapheaders)
{
memset(menuqueue.entries+menuqueue.size, 0, sizeof(roundentry_t));
menuqueue.entries[menuqueue.size].mapnum = map;
menuqueue.entries[menuqueue.size].gametype = levellist.newgametype;
menuqueue.entries[menuqueue.size].encore = (cv_kartencore.value == 1);
menuqueue.size++;
S_StartSound(NULL, sfx_s3k4a);
}
else
{
S_StartSound(NULL, sfx_s3kb2);
}
}
else if (levellist.canqueue && M_MenuExtraPressed(pid))
{
while ((menuqueue.size + roundqueue.size) > ROUNDQUEUE_MAX)
menuqueue.size--;
if (menuqueue.size)
{
S_StartSound(NULL, sfx_shldls);
menuqueue.size--;
}
else if (roundqueue.size)
{
M_StartMessage("Queue Clearing",
va(M_GetText(
"There %s %d Round%s of play queued.\n"
"\n"
"Do you want to empty the queue?\n"
),
(roundqueue.size == 1 ? "is" : "are"),
roundqueue.size,
(roundqueue.size == 1 ? "" : "s")
), &M_ClearQueueResponse, MM_YESNO,
"Time to start fresh",
"Not right now"
);
}
} }
else if (M_MenuBackPressed(pid)) else if (M_MenuBackPressed(pid))
{ {
@ -944,4 +1168,55 @@ void M_LevelSelectHandler(INT32 choice)
void M_LevelSelectTick(void) void M_LevelSelectTick(void)
{ {
if (!menuqueue.sending)
return;
if ((menuqueue.sending <= menuqueue.size) // Sending
&& (roundqueue.size >= menuqueue.anchor)) // Didn't get it wiped
{
if (!netgame)
return;
const UINT8 idcount = (roundqueue.size - menuqueue.anchor);
if (idcount == menuqueue.sending-1)
{
Handle_MapQueueSend(
menuqueue.entries[idcount].mapnum,
menuqueue.entries[idcount].gametype,
menuqueue.entries[idcount].encore
);
}
if (idcount >= menuqueue.sending-1)
{
menuqueue.sending++;
}
return;
}
if (menuqueue.size)
{
S_StartSound(NULL, sfx_chchng);
if (roundqueue.position || gamestate != GS_LEVEL)
{
M_StopMessage(MA_NONE);
}
else
{
M_StartMessage("Round Queue",
va(M_GetText(
"You just queued %d Round%s of play.\n"
"\n"
"Do you want to skip the current\n"
"course and start them immediately?\n"
), menuqueue.size, (menuqueue.size == 1 ? "" : "s")
), &M_MenuQueueResponse, MM_YESNO,
"Let's get going!",
"Not right now"
);
}
}
M_MenuQueueStopSend(MA_NONE);
} }

View file

@ -39,21 +39,21 @@ menuitem_t PAUSE_PlaybackMenu[] =
{IT_CALL | IT_STRING, "Hide Menu", NULL, "M_PHIDE", {.routine = M_SelectableClearMenus}, 0, 0}, {IT_CALL | IT_STRING, "Hide Menu", NULL, "M_PHIDE", {.routine = M_SelectableClearMenus}, 0, 0},
{IT_CALL | IT_STRING, "Restart", NULL, "M_PRSTRT", {.routine = M_PlaybackRewind}, 20, 0}, {IT_CALL | IT_STRING, "Restart", NULL, "M_PRSTRT", {.routine = M_PlaybackRewind}, 20, 0},
{IT_CALL | IT_STRING, "Pause", NULL, "M_PPAUSE", {.routine = M_PlaybackPause}, 36, 0}, {IT_CALL | IT_STRING, "Rewind 5 seconds", NULL, "M_PREW", {.routine = M_PlaybackRewind}, 36, 0},
{IT_CALL | IT_STRING, "Fast-Forward", NULL, "M_PFFWD", {.routine = M_PlaybackFastForward}, 52, 0}, {IT_CALL | IT_STRING, "Pause", NULL, "M_PPAUSE", {.routine = M_PlaybackPause}, 52, 0},
{IT_CALL | IT_STRING, "Restart", NULL, "M_PRSTRT", {.routine = M_PlaybackRewind}, 20, 0}, {IT_CALL | IT_STRING, "Fast-Forward", NULL, "M_PFFWD", {.routine = M_PlaybackFastForward}, 68, 0},
{IT_CALL | IT_STRING, "Resume", NULL, "M_PRESUM", {.routine = M_PlaybackPause}, 36, 0}, {IT_CALL | IT_STRING, "Resume", NULL, "M_PRESUM", {.routine = M_PlaybackPause}, 52, 0},
{IT_CALL | IT_STRING, "Advance Frame", NULL, "M_PFADV", {.routine = M_PlaybackAdvance}, 52, 0}, {IT_CALL | IT_STRING, "Advance Frame", NULL, "M_PFADV", {.routine = M_PlaybackAdvance}, 68, 0},
{IT_ARROWS | IT_STRING, "View Count", NULL, "M_PVIEWS", {.routine = M_PlaybackSetViews}, 72, 0}, {IT_ARROWS | IT_STRING, "View Count", NULL, "M_PVIEWS", {.routine = M_PlaybackSetViews}, 88, 0},
{IT_ARROWS | IT_STRING, "Viewpoint", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 88, 0}, {IT_ARROWS | IT_STRING, "Viewpoint", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 104, 0},
{IT_ARROWS | IT_STRING, "Viewpoint 2", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 104, 0}, {IT_ARROWS | IT_STRING, "Viewpoint 2", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 120, 0},
{IT_ARROWS | IT_STRING, "Viewpoint 3", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 120, 0}, {IT_ARROWS | IT_STRING, "Viewpoint 3", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 136, 0},
{IT_ARROWS | IT_STRING, "Viewpoint 4", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 136, 0}, {IT_ARROWS | IT_STRING, "Viewpoint 4", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 152, 0},
{IT_CALL | IT_STRING, "Toggle Director", NULL, "UN_IC11A", {.routine = M_PlaybackToggleDirector}, 156, 0}, {IT_CALL | IT_STRING, "Toggle Director", NULL, "UN_IC11A", {.routine = M_PlaybackToggleDirector}, 172, 0},
{IT_CALL | IT_STRING, "Toggle Free Camera", NULL, "M_PVIEWS", {.routine = M_PlaybackToggleFreecam}, 172, 0}, {IT_CALL | IT_STRING, "Toggle Free Camera", NULL, "M_PVIEWS", {.routine = M_PlaybackToggleFreecam}, 188, 0},
{IT_CALL | IT_STRING, "Stop Playback", NULL, "M_PEXIT", {.routine = M_PlaybackQuit}, 188, 0}, {IT_CALL | IT_STRING, "Stop Playback", NULL, "M_PEXIT", {.routine = M_PlaybackQuit}, 204, 0},
}; };
menu_t PAUSE_PlaybackMenuDef = { menu_t PAUSE_PlaybackMenuDef = {
@ -151,21 +151,21 @@ static void M_PlaybackTick(void)
playback_last_menu_interaction_leveltime = leveltime - 6*TICRATE; playback_last_menu_interaction_leveltime = leveltime - 6*TICRATE;
// Toggle items // Toggle items
if (paused && !demo.rewinding) if (paused)
{ {
PAUSE_PlaybackMenu[playback_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = PAUSE_PlaybackMenu[playback_rewind].status = IT_DISABLED; PAUSE_PlaybackMenu[playback_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = IT_DISABLED;
PAUSE_PlaybackMenu[playback_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = PAUSE_PlaybackMenu[playback_backframe].status = IT_CALL|IT_STRING; PAUSE_PlaybackMenu[playback_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = IT_CALL|IT_STRING;
if (itemOn >= playback_rewind && itemOn <= playback_fastforward) if (itemOn >= playback_pause && itemOn <= playback_fastforward)
itemOn += playback_backframe - playback_rewind; itemOn += playback_resume - playback_pause;
} }
else else
{ {
PAUSE_PlaybackMenu[playback_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = PAUSE_PlaybackMenu[playback_rewind].status = IT_CALL|IT_STRING; PAUSE_PlaybackMenu[playback_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = IT_CALL|IT_STRING;
PAUSE_PlaybackMenu[playback_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = PAUSE_PlaybackMenu[playback_backframe].status = IT_DISABLED; PAUSE_PlaybackMenu[playback_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = IT_DISABLED;
if (itemOn >= playback_backframe && itemOn <= playback_advanceframe) if (itemOn >= playback_resume && itemOn <= playback_advanceframe)
itemOn -= playback_backframe - playback_rewind; itemOn -= playback_resume - playback_pause;
} }
if (modeattacking) if (modeattacking)
@ -173,10 +173,10 @@ static void M_PlaybackTick(void)
for (i = playback_viewcount; i <= playback_director; i++) for (i = playback_viewcount; i <= playback_director; i++)
PAUSE_PlaybackMenu[i].status = IT_DISABLED; PAUSE_PlaybackMenu[i].status = IT_DISABLED;
PAUSE_PlaybackMenu[playback_freecam].mvar1 = 72; PAUSE_PlaybackMenu[playback_freecam].mvar1 = 88;
PAUSE_PlaybackMenu[playback_quit].mvar1 = 88; PAUSE_PlaybackMenu[playback_quit].mvar1 = 104;
currentMenu->x = BASEVIDWIDTH/2 - 52; currentMenu->x = BASEVIDWIDTH/2 - 60;
} }
else else
{ {
@ -189,11 +189,11 @@ static void M_PlaybackTick(void)
for (i = r_splitscreen+1; i < 4; i++) for (i = r_splitscreen+1; i < 4; i++)
PAUSE_PlaybackMenu[playback_view1+i].status = IT_DISABLED; PAUSE_PlaybackMenu[playback_view1+i].status = IT_DISABLED;
PAUSE_PlaybackMenu[playback_freecam].mvar1 = 172; PAUSE_PlaybackMenu[playback_freecam].mvar1 = 188;
PAUSE_PlaybackMenu[playback_quit].mvar1 = 188; PAUSE_PlaybackMenu[playback_quit].mvar1 = 204;
//currentMenu->x = BASEVIDWIDTH/2 - 94; //currentMenu->x = BASEVIDWIDTH/2 - 102;
currentMenu->x = BASEVIDWIDTH/2 - 96; currentMenu->x = BASEVIDWIDTH/2 - 104;
} }
} }
@ -204,34 +204,38 @@ void M_SetPlaybackMenuPointer(void)
void M_PlaybackRewind(INT32 choice) void M_PlaybackRewind(INT32 choice)
{ {
#if 0 const tic_t curleveltime = leveltime;
static tic_t lastconfirmtime;
(void)choice; if (choice == playback_rewind)
if (!demo.rewinding)
{ {
if (paused) demo.simplerewind = (paused ? DEMO_REWIND_PAUSE : DEMO_REWIND_RESUME);
menuactive = false;
}
G_DoPlayDemo(NULL); // Restart the current demo
if (demo.simplerewind)
{
if (curleveltime > 5*TICRATE)
{ {
G_ConfirmRewind(leveltime-1); g_fast_forward = curleveltime - (5 * TICRATE);
paused = true; g_fast_forward_clock_stop = INFTICS; //I_GetTime() + 2 * TICRATE; -- maybe?
S_PauseAudio();
} }
else else
demo.rewinding = paused = true; {
if (demo.simplerewind == DEMO_REWIND_PAUSE)
{
paused = true;
S_PauseAudio();
}
demo.simplerewind = DEMO_REWIND_OFF;
}
menuactive = true;
} }
else if (lastconfirmtime + TICRATE/2 < I_GetTime()) else
{ {
lastconfirmtime = I_GetTime(); M_ClearMenus(true);
G_ConfirmRewind(leveltime);
} }
CV_SetValue(&cv_playbackspeed, 1);
#else
(void)choice;
G_DoPlayDemo(NULL); // Restart the current demo
M_ClearMenus(true);
#endif
} }
void M_PlaybackPause(INT32 choice) void M_PlaybackPause(INT32 choice)
@ -240,13 +244,7 @@ void M_PlaybackPause(INT32 choice)
paused = !paused; paused = !paused;
if (demo.rewinding) if (paused)
{
G_ConfirmRewind(leveltime);
paused = true;
S_PauseAudio();
}
else if (paused)
S_PauseAudio(); S_PauseAudio();
else else
S_ResumeAudio(); S_ResumeAudio();
@ -258,12 +256,6 @@ void M_PlaybackFastForward(INT32 choice)
{ {
(void)choice; (void)choice;
if (demo.rewinding)
{
G_ConfirmRewind(leveltime);
paused = false;
S_ResumeAudio();
}
CV_SetValue(&cv_playbackspeed, cv_playbackspeed.value == 1 ? 4 : 1); CV_SetValue(&cv_playbackspeed, cv_playbackspeed.value == 1 ? 4 : 1);
} }

View file

@ -8388,6 +8388,46 @@ void P_LoadLevelMusic(void)
Music_ResetLevelVolume(); Music_ResetLevelVolume();
} }
void P_FreeLevelState(void)
{
if (numsectors)
{
F_EndTextPrompt(false, true);
K_UnsetDialogue();
ACS_InvalidateMapScope();
LUA_InvalidateLevel();
Obj_ClearCheckpoints();
sector_t *ss;
for (ss = sectors; sectors+numsectors != ss; ss++)
{
Z_Free(ss->attached);
Z_Free(ss->attachedsolid);
}
// This is the simplest guard against double frees.
// No valid map has zero sectors. Or, come to think
// of it, less than two in general! ~toast 310525
numsectors = 0;
}
// Clear pointers that would be left dangling by the purge
R_FlushTranslationColormapCache();
#ifdef HWRENDER
// Free GPU textures before freeing patches.
if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED))
HWR_ClearAllTextures();
#endif
G_FreeGhosts(); // ghosts are allocated with PU_LEVEL
Patch_FreeTag(PU_PATCH_LOWPRIORITY);
Patch_FreeTag(PU_PATCH_ROTATED);
Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1);
}
/** Loads a level from a lump or external wad. /** Loads a level from a lump or external wad.
* *
* \param fromnetsave If true, skip some stuff because we're loading a netgame snapshot. * \param fromnetsave If true, skip some stuff because we're loading a netgame snapshot.
@ -8401,7 +8441,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
// 99% of the things already did, so. // 99% of the things already did, so.
// Map header should always be in place at this point // Map header should always be in place at this point
INT32 i, ranspecialwipe = 0; INT32 i, ranspecialwipe = 0;
sector_t *ss;
virtlump_t *encoreLump = NULL; virtlump_t *encoreLump = NULL;
levelloading = true; levelloading = true;
@ -8474,7 +8513,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
wipegamestate = gamestate; // Don't fade if reloading the gamestate wipegamestate = gamestate; // Don't fade if reloading the gamestate
// Encore mode fade to pink to white // Encore mode fade to pink to white
// This is handled BEFORE sounds are stopped. // This is handled BEFORE sounds are stopped.
else if (encoremode && !prevencoremode && modeattacking == ATTACKING_NONE && !demo.rewinding) else if (encoremode && !prevencoremode && !demo.simplerewind)
{ {
if (rendermode != render_none) if (rendermode != render_none)
{ {
@ -8545,7 +8584,14 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
// Let's fade to white here // Let's fade to white here
// But only if we didn't do the encore startup wipe // But only if we didn't do the encore startup wipe
if (!demo.rewinding && !reloadinggamestate) if (demo.attract || demo.simplerewind)
{
// Leave the music alone! We're already playing what we want!
// Pull from RNG even though music will never change
// To silence playback has desynced warning
P_Random(PR_MUSICSELECT);
}
else if (!reloadinggamestate)
{ {
int wipetype = wipe_level_toblack; int wipetype = wipe_level_toblack;
@ -8558,15 +8604,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)); FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE));
#endif #endif
if (demo.attract) if (K_PodiumSequence())
{
; // Leave the music alone! We're already playing what we want!
// Pull from RNG even though music will never change
// To silence playback has desynced warning
P_Random(PR_MUSICSELECT);
}
else if (K_PodiumSequence())
{ {
// mapmusrng is set by local player position in K_ResetCeremony // mapmusrng is set by local player position in K_ResetCeremony
P_LoadLevelMusic(); P_LoadLevelMusic();
@ -8643,44 +8681,42 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
} }
F_RunWipe(wipetype, wipedefs[wipetype], false, ((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), false, false); F_RunWipe(wipetype, wipedefs[wipetype], false, ((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), false, false);
// Hold respawn to keep waiting until you're ready
if (G_IsModeAttackRetrying() && !demo.playback)
{
nowtime = lastwipetic;
while (G_PlayerInputDown(0, gc_respawn, splitscreen + 1) == true)
{
while (!((nowtime = I_GetTime()) - lastwipetic))
{
I_Sleep(cv_sleep.value);
I_UpdateTime();
} \
I_OsPolling();
G_ResetAllDeviceResponding();
for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
{
HandleGamepadDeviceEvents(&events[eventtail]);
G_MapEventsToControls(&events[eventtail]);
}
lastwipetic = nowtime;
if (moviemode && rendermode == render_opengl)
M_LegacySaveFrame();
else if (moviemode && rendermode == render_soft)
I_CaptureVideoFrame();
NetKeepAlive();
}
//wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE);
}
} }
} }
/* P_FreeLevelState();
if (!titlemapinaction)
wipegamestate = GS_LEVEL;
*/
// Close text prompt before freeing the old level
F_EndTextPrompt(false, true);
K_UnsetDialogue();
ACS_InvalidateMapScope();
LUA_InvalidateLevel();
Obj_ClearCheckpoints();
for (ss = sectors; sectors+numsectors != ss; ss++)
{
Z_Free(ss->attached);
Z_Free(ss->attachedsolid);
}
// Clear pointers that would be left dangling by the purge
R_FlushTranslationColormapCache();
#ifdef HWRENDER
// Free GPU textures before freeing patches.
if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED))
HWR_ClearAllTextures();
#endif
G_FreeGhosts(); // ghosts are allocated with PU_LEVEL
Patch_FreeTag(PU_PATCH_LOWPRIORITY);
Patch_FreeTag(PU_PATCH_ROTATED);
Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1);
R_InitializeLevelInterpolators(); R_InitializeLevelInterpolators();

View file

@ -107,6 +107,7 @@ extern mapthing_t *mapthings;
void P_SetupLevelSky(const char *skytexname, boolean global); void P_SetupLevelSky(const char *skytexname, boolean global);
void P_RespawnThings(void); void P_RespawnThings(void);
void P_FreeLevelState(void);
void P_ResetLevelMusic(void); void P_ResetLevelMusic(void);
boolean P_UseContinuousLevelMusic(void); boolean P_UseContinuousLevelMusic(void);
void P_LoadLevelMusic(void); void P_LoadLevelMusic(void);

View file

@ -739,15 +739,7 @@ void P_Ticker(boolean run)
// Check for pause or menu up in single player // Check for pause or menu up in single player
if (paused || P_AutoPause()) if (paused || P_AutoPause())
{ {
if (demo.rewinding && leveltime > 0) P_RunChaseCameras(); // special case: allow freecam to MOVE during pause!
{
leveltime = (leveltime-1) & ~3;
if (timeinmap > 0)
timeinmap = (timeinmap-1) & ~3;
G_PreviewRewind(leveltime);
}
else
P_RunChaseCameras(); // special case: allow freecam to MOVE during pause!
return; return;
} }
@ -762,17 +754,23 @@ void P_Ticker(boolean run)
if (demo.recording) if (demo.recording)
{ {
G_WriteDemoExtraData(); if (!G_ConsiderEndingDemoWrite())
for (i = 0; i < MAXPLAYERS; i++) {
if (playeringame[i]) G_WriteDemoExtraData();
G_WriteDemoTiccmd(&players[i].cmd, i); for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
G_WriteDemoTiccmd(&players[i].cmd, i);
}
} }
if (demo.playback && !demo.waitingfortally) if (demo.playback && !demo.waitingfortally)
{ {
G_ReadDemoExtraData(); if (!G_ConsiderEndingDemoRead())
for (i = 0; i < MAXPLAYERS; i++) {
if (playeringame[i]) G_ReadDemoExtraData();
G_ReadDemoTiccmd(&players[i].cmd, i); for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
G_ReadDemoTiccmd(&players[i].cmd, i);
}
} }
LUA_ResetTicTimers(); LUA_ResetTicTimers();
@ -1078,7 +1076,7 @@ void P_Ticker(boolean run)
} }
} }
if (g_fast_forward == 0) if (g_fast_forward == 0 || demo.simplerewind)
{ {
timeinmap++; timeinmap++;
} }
@ -1168,14 +1166,21 @@ void P_Ticker(boolean run)
if (demo.recording) if (demo.recording)
{ {
G_WriteAllGhostTics();
if (cv_recordmultiplayerdemos.value && demo.savebutton && demo.savebutton + 3*TICRATE < leveltime) if (cv_recordmultiplayerdemos.value && demo.savebutton && demo.savebutton + 3*TICRATE < leveltime)
G_CheckDemoTitleEntry(); G_CheckDemoTitleEntry();
if (!G_ConsiderEndingDemoWrite())
{
G_WriteAllGhostTics();
}
} }
else if (demo.playback && !demo.waitingfortally) // Use Ghost data for consistency checks. else if (demo.playback && !demo.waitingfortally)
{ {
G_ConsAllGhostTics(); if (!G_ConsiderEndingDemoRead())
{
// Use Ghost data for consistency checks.
G_ConsAllGhostTics();
}
} }
if (modeattacking) if (modeattacking)
@ -1238,9 +1243,6 @@ void P_Ticker(boolean run)
P_MapEnd(); P_MapEnd();
if (demo.playback)
G_StoreRewindInfo();
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
G_CopyTiccmd(&players[i].oldcmd, &players[i].cmd, 1); G_CopyTiccmd(&players[i].oldcmd, &players[i].cmd, 1);

View file

@ -737,6 +737,8 @@ void ST_startTitleCard(void)
lt_ticker = lt_exitticker = lt_lasttic = 0; lt_ticker = lt_exitticker = lt_lasttic = 0;
lt_endtime = 4*TICRATE; // + (10*NEWTICRATERATIO); lt_endtime = 4*TICRATE; // + (10*NEWTICRATERATIO);
lt_fade = 0; lt_fade = 0;
WipeStageTitle = false;
} }
// //
@ -788,7 +790,7 @@ patch_t *ST_getRoundPicture(boolean small)
// //
void ST_runTitleCard(void) void ST_runTitleCard(void)
{ {
boolean run = !(paused || P_AutoPause() || g_fast_forward > 0); boolean run = !(paused || P_AutoPause() || (g_fast_forward > 0 && demo.simplerewind == DEMO_REWIND_OFF));
INT32 auxticker; INT32 auxticker;
boolean doroundicon = (ST_getRoundPicture(false) != NULL); boolean doroundicon = (ST_getRoundPicture(false) != NULL);

View file

@ -76,7 +76,6 @@ TYPEDEF (plrconfig);
TYPEDEF (filesneededconfig_pak); TYPEDEF (filesneededconfig_pak);
TYPEDEF (doomdata_t); TYPEDEF (doomdata_t);
TYPEDEF (serverelem_t); TYPEDEF (serverelem_t);
TYPEDEF (rewind_t);
TYPEDEF (clientkey_pak); TYPEDEF (clientkey_pak);
TYPEDEF (serverchallenge_pak); TYPEDEF (serverchallenge_pak);
TYPEDEF (challengeall_pak); TYPEDEF (challengeall_pak);

View file

@ -971,11 +971,15 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
// Handles drawing the bottom-of-screen progression. // Handles drawing the bottom-of-screen progression.
// Currently requires intermission y_data for animation only. // Currently requires intermission y_data for animation only.
// //
void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, boolean widescreen) void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, boolean widescreen, boolean adminmode)
{ {
if (roundqueue.size == 0) if (roundqueue.size == 0)
{ {
return; if (!adminmode
|| menuqueue.size == 0)
{
return;
}
} }
// The following is functionally a hack. // The following is functionally a hack.
@ -1041,6 +1045,10 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
prize_dot[BPP_AHEAD] = static_cast<patch_t*>(W_CachePatchName("R_RRMRK4", PU_PATCH)); prize_dot[BPP_AHEAD] = static_cast<patch_t*>(W_CachePatchName("R_RRMRK4", PU_PATCH));
prize_dot[BPP_DONE] = static_cast<patch_t*>(W_CachePatchName("R_RRMRK6", PU_PATCH)); prize_dot[BPP_DONE] = static_cast<patch_t*>(W_CachePatchName("R_RRMRK6", PU_PATCH));
patch_t *rpmark[2];
rpmark[0] = static_cast<patch_t*>(W_CachePatchName("R_RPMARK", PU_PATCH));
rpmark[1] = static_cast<patch_t*>(W_CachePatchName("R_R2MARK", PU_PATCH));
UINT8 *colormap = NULL, *oppositemap = NULL; UINT8 *colormap = NULL, *oppositemap = NULL;
fixed_t playerx = 0, playery = 0; fixed_t playerx = 0, playery = 0;
UINT8 pskin = MAXSKINS; UINT8 pskin = MAXSKINS;
@ -1079,10 +1087,37 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
upwa = true; upwa = true;
} }
workingqueuesize--; if (!adminmode)
{
workingqueuesize--;
}
} }
INT32 widthofroundqueue = 24*(workingqueuesize - 1); INT32 widthofroundqueue, totalsteps;
INT32 menusendoffset = 0;
if (menuqueue.sending)
{
if (menuqueue.sending > menuqueue.size)
{
menusendoffset = menuqueue.size;
}
else
{
menusendoffset = menuqueue.sending-1;
}
}
if (adminmode)
{
totalsteps = std::min(workingqueuesize + (menuqueue.size - menusendoffset), ROUNDQUEUE_MAX);
}
else
{
totalsteps = workingqueuesize;
}
widthofroundqueue = (totalsteps - 1) * 24;
INT32 x = (BASEVIDWIDTH - widthofroundqueue) / 2; INT32 x = (BASEVIDWIDTH - widthofroundqueue) / 2;
INT32 y, basey = 167 + offset; INT32 y, basey = 167 + offset;
@ -1091,7 +1126,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
// The following block handles horizontal easing of the // The following block handles horizontal easing of the
// progression bar on the last non-rankrestricted round. // progression bar on the last non-rankrestricted round.
if (standings->showrank == true) if (!adminmode && standings->showrank == true)
{ {
fixed_t percentslide = 0; fixed_t percentslide = 0;
SINT8 deferxoffs = 0; SINT8 deferxoffs = 0;
@ -1151,12 +1186,22 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
V_DrawMappedPatch(xiter, basey, baseflags, queuebg_upwa, greymap); V_DrawMappedPatch(xiter, basey, baseflags, queuebg_upwa, greymap);
} }
// Draw to left side of screen
while (xiter > -bufferspace) while (xiter > -bufferspace)
{ {
xiter -= 24; xiter -= 24;
V_DrawMappedPatch(xiter, basey, baseflags, queuebg_flat, greymap); V_DrawMappedPatch(xiter, basey, baseflags, queuebg_flat, greymap);
} }
// Draw to right side of screen
xiter = x + widthofroundqueue;
while (xiter < BASEVIDWIDTH + bufferspace)
{
xiter += 24;
V_DrawMappedPatch(xiter, basey, baseflags, queuebg_flat, greymap);
}
// Actually queued maps
for (i = 0; i < workingqueuesize; i++) for (i = 0; i < workingqueuesize; i++)
{ {
// Draw the background, and grab the appropriate line, to the right of the dot // Draw the background, and grab the appropriate line, to the right of the dot
@ -1186,7 +1231,13 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
} }
else else
{ {
V_DrawMappedPatch(x, basey, baseflags, queuebg_flat, greymap); V_DrawMappedPatch(
x,
basey,
baseflags,
((workingqueuesize == totalsteps) ? queuebg_flat : queuebg_upwa),
greymap
);
} }
} }
@ -1321,17 +1372,9 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
} }
else else
{ {
// No more line! Fill in background to right edge of screen
xiter = x;
while (xiter < BASEVIDWIDTH + bufferspace)
{
xiter += 24;
V_DrawMappedPatch(xiter, basey, baseflags, queuebg_flat, greymap);
}
// Handle special entry on the end // Handle special entry on the end
// (has to be drawn before the semifinal dot due to overlap) // (has to be drawn before the semifinal dot due to overlap)
if (standings->showrank == true) if (!adminmode && standings->showrank == true)
{ {
const fixed_t x2 = x + spacetospecial; const fixed_t x2 = x + spacetospecial;
@ -1342,7 +1385,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
} }
else if ( else if (
doanimations == true doanimations == true
&& roundqueue.position == roundqueue.size-1 && roundqueue.position == workingqueuesize
&& timer - interpoffs <= 2*TICRATE && timer - interpoffs <= 2*TICRATE
) )
{ {
@ -1522,13 +1565,62 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
x += 24; x += 24;
} }
totalsteps -= i;
// Maps in the progress of being queued on the menu
if (adminmode && totalsteps)
{
for (i = menusendoffset; i < (totalsteps + menusendoffset); i++)
{
upwa ^= true;
if (upwa == false)
{
y = basey + 4;
V_DrawMappedPatch(x, basey, baseflags, queuebg_down, greymap);
}
else
{
y = basey + 12;
if (i+1 != menuqueue.size) // no more line?
{
V_DrawMappedPatch(x, basey, baseflags, queuebg_upwa, greymap);
}
else
{
V_DrawMappedPatch(x, basey, baseflags, queuebg_flat, greymap);
}
}
V_DrawMappedPatch(
x - 8, y,
baseflags,
level_dot[BPP_AHEAD],
NULL
);
V_DrawMappedPatch(
x - 10, y - 14,
baseflags,
rpmark[0],
NULL
);
K_DrawMapAsFace(
x - 9, y - 13,
(baseflags|((menuqueue.entries[i].encore) ? V_FLIP : 0)),
menuqueue.entries[i].mapnum,
NULL
);
x += 24;
}
}
// Draw the player position through the round queue! // Draw the player position through the round queue!
if (playery != 0) if (playery != 0)
{ {
patch_t *rpmark[2];
rpmark[0] = static_cast<patch_t*>(W_CachePatchName("R_RPMARK", PU_PATCH));
rpmark[1] = static_cast<patch_t*>(W_CachePatchName("R_R2MARK", PU_PATCH));
// Change alignment // Change alignment
playerx -= (10 * FRACUNIT); playerx -= (10 * FRACUNIT);
playery -= (14 * FRACUNIT); playery -= (14 * FRACUNIT);
@ -1934,7 +2026,7 @@ skiptallydrawer:
goto finalcounter; goto finalcounter;
// Returns early if there's no roundqueue entries to draw // Returns early if there's no roundqueue entries to draw
Y_RoundQueueDrawer(&data, 0, true, false); Y_RoundQueueDrawer(&data, 0, true, false, false);
if (netgame) if (netgame)
{ {

View file

@ -54,7 +54,7 @@ void Y_Ticker(void);
// Specific sub-drawers // Specific sub-drawers
void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset); void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset);
void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, boolean widescreen); void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, boolean widescreen, boolean adminmode);
void Y_DrawIntermissionButton(INT32 startslide, INT32 through, boolean widescreen); void Y_DrawIntermissionButton(INT32 startslide, INT32 through, boolean widescreen);
void Y_DrawRankMode(INT32 x, INT32 y, boolean center); void Y_DrawRankMode(INT32 x, INT32 y, boolean center);