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];
}
#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)
{
#ifdef NOMD5

View file

@ -712,21 +712,6 @@ extern boolean hu_stopped;
// 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 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)
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.recalc = 0;
@ -936,7 +933,7 @@ void D_SRB2Loop(void)
realtics = entertic - oldentertics;
oldentertics = entertic;
if (demo.playback && gamestate == GS_LEVEL)
if (demo.playback && gamestate == GS_LEVEL && demo.simplerewind == DEMO_REWIND_OFF)
{
// Nicer place to put this.
realtics = realtics * cv_playbackspeed.value;
@ -1216,6 +1213,7 @@ void D_ClearState(void)
// Reset GP and roundqueue
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
memset(&roundqueue, 0, sizeof(struct roundqueue));
memset(&menuqueue, 0, sizeof(struct menuqueue));
// empty some other semi-important state
maptol = 0;
@ -1248,7 +1246,8 @@ void D_ClearState(void)
if (gamedata && gamedata->deferredsave)
G_SaveGameData();
K_UnsetDialogue();
P_FreeLevelState();
P_InvalidateThinkersWithoutInit();
G_SetGamestate(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);
}
static void Handle_MapQueueSend(UINT16 newmapnum, UINT16 newgametype, boolean newencoremode)
void Handle_MapQueueSend(UINT16 newmapnum, UINT16 newgametype, boolean newencoremode)
{
UINT8 flags = 0;

View file

@ -206,6 +206,7 @@ size_t WeaponPref_Parse(const UINT8 *p, INT32 playernum);
void D_SendPlayerConfig(UINT8 n);
void Command_ExitGame_f(void);
void Command_Retry_f(void);
void Handle_MapQueueSend(UINT16 newmapnum, UINT16 newgametype, boolean newencoremode);
boolean G_GamestateUsesExitLevel(void);
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);

View file

@ -404,7 +404,10 @@ class TiccmdBuilder
map(gc_item, BT_ATTACK); // fire
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
// lua buttons a thru c

View file

@ -270,22 +270,31 @@ static ticcmd_t oldcmd[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)
{
INT32 p, extradata, i;
char name[64];
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);
p = READUINT8(demobuf.p);
@ -460,13 +469,6 @@ void G_ReadDemoExtraData(void)
p = READUINT8(demobuf.p);
}
if (!(demoflags & DF_GHOST) && *demobuf.p == DEMOMARKER)
{
// end of demo data stream
G_CheckDemoStatus();
return;
}
}
void G_WriteDemoExtraData(void)
@ -623,13 +625,6 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
}
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)
@ -739,14 +734,6 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
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)
@ -794,8 +781,11 @@ void G_GhostAddHit(INT32 playernum, mobj_t *victim)
void G_WriteAllGhostTics(void)
{
boolean toobig = false;
INT32 i, counter = leveltime;
if (!demobuf.p || !(demoflags & DF_GHOST))
return; // No ghost data to write.
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
@ -811,22 +801,9 @@ void G_WriteAllGhostTics(void)
WRITEUINT8(demobuf.p, 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);
if (toobig)
{
G_CheckDemoStatus(); // no more space
return;
}
}
void G_WriteGhostTic(mobj_t *ghost, INT32 playernum)
@ -835,11 +812,6 @@ void G_WriteGhostTic(mobj_t *ghost, INT32 playernum)
UINT8 *ziptic_p;
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
#define MAXMOM (0xFFFF<<8)
@ -1061,7 +1033,7 @@ void G_ConsAllGhostTics(void)
{
UINT8 p;
if (!demobuf.p || !demo.deferstart)
if (!demobuf.p || !(demoflags & DF_GHOST) || !demo.deferstart)
return;
p = READUINT8(demobuf.p);
@ -1071,13 +1043,6 @@ void G_ConsAllGhostTics(void)
G_ConsGhostTic(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.
@ -1089,9 +1054,6 @@ void G_ConsGhostTic(INT32 playernum)
mobj_t *testmo;
UINT32 syncleeway;
if (!(demoflags & DF_GHOST))
return; // No ghost data to use.
testmo = players[playernum].mo;
// 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)
@ -1309,11 +1264,31 @@ void G_GhostTicker(void)
continue;
readghosttic:
#define follow g->mo->tracer
// Skip normal demo data.
ziptic = READUINT8(g->p);
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
{
if (ziptic < MAXPLAYERS)
@ -1414,9 +1389,11 @@ readghosttic:
// Grab ghost data.
ziptic = READUINT8(g->p);
if (ziptic == DEMOMARKER) // Had to end early for some reason
goto fadeghost;
if (ziptic == 0xFF)
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
ziptic = READUINT8(g->p);
@ -1530,7 +1507,6 @@ readghosttic:
g->mo->renderflags &= ~RF_DONTDRAW;
}
#define follow g->mo->tracer
if (ziptic & GZT_FOLLOW)
{ // Even more...
UINT8 followtic = READUINT8(g->p);
@ -1620,28 +1596,6 @@ skippedghosttic:
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
// 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 (starttime <= leveltime && !g->linecrossed && G_TimeAttackStart())
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
//
@ -1885,7 +1642,7 @@ void G_RecordDemo(const char *name)
functions will check if they overran the buffer, but it
should be safe enough because they'll think there's less
memory than there actually is and stop early. */
const size_t deadspace = 1024;
const size_t deadspace = 2048;
I_Assert(demobuf.size > deadspace);
demobuf.size -= deadspace;
demobuf.end -= deadspace;
@ -2999,8 +2756,6 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
boolean skiperrors = true;
#endif
G_InitDemoRewind();
gtname[MAXGAMETYPELENGTH-1] = '\0';
if (deflumpnum != LUMPERROR)
@ -3363,22 +3118,6 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
// Load "mapmusrng" used for altmusic selection
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(&oldghost,0,sizeof(oldghost));
memset(&ghostext,0,sizeof(ghostext));
@ -3607,9 +3346,26 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
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;
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)
@ -3756,14 +3512,6 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname)
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
// any invalidating flags?
@ -3811,6 +3559,14 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname)
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->sizes = ghostsizes;
@ -4173,7 +3929,7 @@ boolean G_CheckDemoStatus(void)
// Keep the demo open and don't boot to intermission
// YET, pause demo playback.
if (!demo.waitingfortally && modeattacking && exitcountdown)
demo.waitingfortally = true;
;
else if (!demo.attract)
G_FinishExitLevel();
else
@ -4191,6 +3947,8 @@ boolean G_CheckDemoStatus(void)
D_SetDeferredStartTitle(true);
}
demo.waitingfortally = true; // if we've returned early for some reason...
return true;
}
@ -4235,6 +3993,13 @@ void G_SaveDemo(void)
if (currentMenu == &TitleEntryDef)
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.
if (demoinfo_p && *(UINT32 *)demoinfo_p == 0)
{

View file

@ -84,7 +84,7 @@ struct demovars_s {
boolean recording, playback, timing;
UINT16 version; // Current file format of the demo being played
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 quitafterplaying; // quit after playing a demo from cmdline
@ -172,6 +172,8 @@ extern UINT8 demo_writerng;
boolean G_CompatLevel(UINT16 level);
// Record/playback tics
boolean G_ConsiderEndingDemoRead(void);
boolean G_ConsiderEndingDemoWrite(void);
void G_ReadDemoExtraData(void);
void G_WriteDemoExtraData(void);
void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum);
@ -186,11 +188,6 @@ void G_ConsAllGhostTics(void);
void G_ConsGhostTic(INT32 playernum);
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
{
size_t player_name;
@ -250,6 +247,13 @@ typedef enum
DEMO_ATTRACT_CREDITS
} demoAttractMode_t;
typedef enum
{
DEMO_REWIND_OFF = 0,
DEMO_REWIND_RESUME,
DEMO_REWIND_PAUSE
} demoRewindMode_t;
void G_SyncDemoParty(INT32 rem, INT32 newsplitscreen);
#ifdef __cplusplus

View file

@ -1245,26 +1245,36 @@ void G_StartTitleCard(void)
// prepare status bar
ST_startTitleCard(); // <-- always must be called to init some variables
// The title card has been disabled for this map.
// Oh well.
if (demo.rewinding || !G_IsTitleCardAvailable())
{
WipeStageTitle = false;
if (demo.simplerewind)
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
WipeStageTitle = (gamestate == GS_LEVEL);
// play the sound
if (WipeStageTitle)
if (WipeStageTitle && !kstart)
{
sfxenum_t kstart = sfx_kstart;
if (K_CheckBossIntro() == true)
kstart = sfx_ssa021;
else if (encoremode == true)
kstart = sfx_ruby2;
S_StartSound(NULL, kstart);
// Play the standard titlecard sound
S_StartSound(NULL, sfx_kstart);
}
}
@ -1439,13 +1449,7 @@ boolean G_Responder(event_t *ev)
{
paused = !paused;
if (demo.rewinding)
{
G_ConfirmRewind(leveltime);
paused = true;
S_PauseAudio();
}
else if (paused)
if (paused)
S_PauseAudio();
else
S_ResumeAudio();
@ -2150,6 +2154,14 @@ void G_Ticker(boolean run)
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.
g_fast_forward_clock_stop = INFTICS;
}
@ -4094,6 +4106,7 @@ doremove:
// Next map apparatus
struct roundqueue roundqueue;
struct menuqueue menuqueue;
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();
}
prevencoremode = ((!Playing()) ? false : encoremode);
prevencoremode = encoremode;
encoremode = pencoremode;
legitimateexit = false; // SRB2Kart
@ -5829,7 +5842,7 @@ boolean G_GetExitGameFlag(void)
// Same deal with retrying.
void G_SetRetryFlag(void)
{
if (retrying == false)
if (retrying == false && grandprixinfo.gp)
{
grandprixinfo.rank.continuesUsed++;
}

View file

@ -75,6 +75,15 @@ extern struct roundqueue
roundentry_t entries[ROUNDQUEUE_MAX]; // Entries in the round queue
} 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_MapIntoRoundQueue(UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted);
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
static void K_drawKartItem(void)
{

View file

@ -60,6 +60,7 @@ void K_drawKart4PTimestamp(void);
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_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_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);

View file

@ -593,10 +593,10 @@ extern menu_t PAUSE_PlaybackMenuDef;
typedef enum
{
playback_hide,
playback_restart,
playback_rewind,
playback_pause,
playback_fastforward,
playback_backframe,
playback_resume,
playback_advanceframe,
playback_viewcount,
@ -923,6 +923,7 @@ typedef struct levellist_s {
UINT8 guessgt;
levelsearch_t levelsearch;
boolean netgame; // Start the game in an actual server
boolean canqueue;
menu_t *backMenu;
} levellist_t;
@ -944,6 +945,8 @@ void M_CupSelectTick(void);
void M_LevelSelectHandler(INT32 choice);
void M_LevelSelectTick(void);
INT16 M_LevelFromScrolledList(INT16 add);
void M_MenuToLevelPreamble(UINT8 ssplayers, boolean nowipe);
void M_LevelSelected(INT16 add, boolean menuupdate);
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_DrawMediocreKeyboardKey(const char *text, INT32 *workx, INT32 worky, boolean push, boolean rightaligned)
static void M_DrawPauseRoundQueue(INT16 offset, boolean canqueue)
{
INT32 buttonwidth = V_StringWidth(text, 0) + 2;
y_data_t standings;
memset(&standings, 0, sizeof (standings));
if (rightaligned)
if (gamestate == GS_MENU)
{
(*workx) -= buttonwidth;
}
if (push)
{
worky += 2;
standings.mainplayer = MAXPLAYERS;
}
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);
V_DrawString(
(*workx), worky + 1,
0, text
);
// See also G_GetNextMap, Y_CalculateMatchData
if (
canqueue == false
&& 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
void M_DrawMenuMessage(void)
@ -991,6 +998,16 @@ void M_Drawer(void)
// Draw message overlay when needed
M_DrawMenuMessage();
if (
(
currentMenu == &PLAY_LevelSelectDef
|| currentMenu == &PLAY_CupSelectDef
) && levellist.canqueue
)
{
M_DrawPauseRoundQueue(0, true);
}
}
if (menuwipe)
@ -6288,30 +6305,11 @@ void M_DrawPause(void)
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)
{
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)
{
patch_t *smallroundpatch = ST_getRoundPicture(true);
@ -6328,7 +6326,7 @@ void M_DrawPause(void)
V_DrawCenteredMenuString(24, 167 + offset/2, V_YELLOWMAP, M_GetGameplayMode());
Y_RoundQueueDrawer(&standings, offset/2, false, false);
drawqueue = true;
}
else if (gametype == GT_TUTORIAL)
{
@ -6347,6 +6345,11 @@ void M_DrawPause(void)
{
V_DrawMenuString(4, 188 + offset/2, V_YELLOWMAP, M_GetGameplayMode());
}
if (drawqueue)
{
M_DrawPauseRoundQueue(offset/2, rulescheck);
}
}
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)
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));
else
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)
{
// 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);
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
{
const fixed_t iconHeight = (14 << FRACBITS);
const fixed_t iconWidth = (iconHeight * 320) / 200;
V_DrawFill(
K_DrawMapAsFace(
fx + fw - whiteSq + dupx,
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),
g_voteLevels[v][0],
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
levellist.netgame = false;
levellist.canqueue = false;
levellist.levelsearch.checklocked = true;
levellist.levelsearch.grandprix = false;
levellist.levelsearch.timeattack = false;

View file

@ -614,34 +614,19 @@ void M_StartTimeAttack(INT32 choice)
{
modeattacking |= ATTACKING_SPB;
}
if (gamestate == GS_MENU)
{
encoremode = true; // guarantees short wipe
}
modeprefix = "spb-";
}
// DON'T SOFTLOCK
CON_ToggleOff();
// Still need to reset devmode
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);
M_MenuToLevelPreamble(0, (gamestate != GS_MENU || cv_dummyspbattack.value == 1));
gpath = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s",
srb2home, timeattackfolder);
@ -659,8 +644,17 @@ void M_StartTimeAttack(INT32 choice)
restoreMenu = &PLAY_TimeAttackDef;
D_MapChange(
levellist.choosemap+1,
levellist.newgametype,
(cv_dummyspbattack.value == 1),
true,
1,
false,
false
);
M_ClearMenus(true);
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_dummyspbattack.value == 1), 1, 1, false, false);
G_UpdateTimeStickerMedals(levellist.choosemap, true);
}

View file

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

View file

@ -57,26 +57,7 @@ static void M_StartCup(UINT8 entry)
entry = UINT8_MAX;
}
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);
if (cv_maxconnections.value < ssplayers+1)
CV_SetValue(&cv_maxconnections, ssplayers+1);
if (splitscreen != ssplayers)
{
splitscreen = ssplayers;
SplitScreen_OnChange();
}
M_MenuToLevelPreamble(ssplayers, false);
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);
restoreMenu = &PLAY_CupSelectDef;

View file

@ -21,6 +21,7 @@
#include "../../v_video.h"
#include "../../g_game.h" // G_GetBackupCupData
#include "../../p_saveg.h" // cupsavedata
#include "../../d_netcmd.h" // Handle_MapQueueSend
cupheader_t dummy_lostandfound;
@ -657,14 +658,22 @@ void M_LevelSelectInit(INT32 choice)
case 0:
levellist.levelsearch.grandprix = 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;
case 1:
levellist.levelsearch.grandprix = false;
levellist.levelsearch.timeattack = true;
levellist.canqueue = false;
break;
case 2:
levellist.levelsearch.grandprix = true;
levellist.levelsearch.timeattack = false;
levellist.canqueue = false;
break;
default:
CONS_Alert(CONS_WARNING, "Bad level select init\n");
@ -676,10 +685,14 @@ void M_LevelSelectInit(INT32 choice)
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);
S_StartSound(NULL, sfx_s3k81);
const INT32 skinid = R_SkinAvailableEx(cv_skin[0].string, false);
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))
@ -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;
INT16 map = M_GetFirstLevelInList(&i, &levellist.levelsearch);
@ -706,6 +753,13 @@ void M_LevelSelected(INT16 add, boolean menuupdate)
add--;
}
return map;
}
void M_LevelSelected(INT16 add, boolean menuupdate)
{
INT16 map = M_LevelFromScrolledList(add);
if (map >= nummapheaders)
{
// This shouldn't happen
@ -733,75 +787,116 @@ void M_LevelSelected(INT16 add, boolean menuupdate)
{
if (gamestate == GS_MENU)
{
UINT8 ssplayers = levellist.levelsearch.tutorial ? 0 : cv_splitplayers.value-1;
netgame = false;
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
cht_debug = 0;
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;
}
restoreMenu = (levellist.netgame)
? &PLAY_MP_OptSelectDef
: currentMenu;
restorelevellist = levellist;
}
else
{
// directly do the map change
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false);
}
D_MapChange(
levellist.choosemap+1,
levellist.newgametype,
(cv_kartencore.value == 1),
true,
1,
false,
false
);
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)
{
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)
{
const UINT8 pid = 0;
@ -894,6 +1027,11 @@ void M_LevelSelectHandler(INT32 choice)
return;
}
if (menuqueue.sending)
{
return;
}
if (menucmd[pid].dpad_ud > 0)
{
levellist.cursor++;
@ -926,10 +1064,96 @@ void M_LevelSelectHandler(INT32 choice)
M_LevelSelectScrollDest();
if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/)
if (M_MenuConfirmPressed(pid))
{
// Starting immediately OR importing queue
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))
{
@ -944,4 +1168,55 @@ void M_LevelSelectHandler(INT32 choice)
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, "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, "Fast-Forward", NULL, "M_PFFWD", {.routine = M_PlaybackFastForward}, 52, 0},
{IT_CALL | IT_STRING, "Restart", NULL, "M_PRSTRT", {.routine = M_PlaybackRewind}, 20, 0},
{IT_CALL | IT_STRING, "Resume", NULL, "M_PRESUM", {.routine = M_PlaybackPause}, 36, 0},
{IT_CALL | IT_STRING, "Advance Frame", NULL, "M_PFADV", {.routine = M_PlaybackAdvance}, 52, 0},
{IT_CALL | IT_STRING, "Rewind 5 seconds", NULL, "M_PREW", {.routine = M_PlaybackRewind}, 36, 0},
{IT_CALL | IT_STRING, "Pause", NULL, "M_PPAUSE", {.routine = M_PlaybackPause}, 52, 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}, 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, "Viewpoint", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 88, 0},
{IT_ARROWS | IT_STRING, "Viewpoint 2", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 104, 0},
{IT_ARROWS | IT_STRING, "Viewpoint 3", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 120, 0},
{IT_ARROWS | IT_STRING, "Viewpoint 4", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 136, 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}, 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}, 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 Free Camera", NULL, "M_PVIEWS", {.routine = M_PlaybackToggleFreecam}, 172, 0},
{IT_CALL | IT_STRING, "Stop Playback", NULL, "M_PEXIT", {.routine = M_PlaybackQuit}, 188, 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}, 188, 0},
{IT_CALL | IT_STRING, "Stop Playback", NULL, "M_PEXIT", {.routine = M_PlaybackQuit}, 204, 0},
};
menu_t PAUSE_PlaybackMenuDef = {
@ -151,21 +151,21 @@ static void M_PlaybackTick(void)
playback_last_menu_interaction_leveltime = leveltime - 6*TICRATE;
// 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_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = PAUSE_PlaybackMenu[playback_backframe].status = IT_CALL|IT_STRING;
PAUSE_PlaybackMenu[playback_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = IT_DISABLED;
PAUSE_PlaybackMenu[playback_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = IT_CALL|IT_STRING;
if (itemOn >= playback_rewind && itemOn <= playback_fastforward)
itemOn += playback_backframe - playback_rewind;
if (itemOn >= playback_pause && itemOn <= playback_fastforward)
itemOn += playback_resume - playback_pause;
}
else
{
PAUSE_PlaybackMenu[playback_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = PAUSE_PlaybackMenu[playback_rewind].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_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = IT_CALL|IT_STRING;
PAUSE_PlaybackMenu[playback_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = IT_DISABLED;
if (itemOn >= playback_backframe && itemOn <= playback_advanceframe)
itemOn -= playback_backframe - playback_rewind;
if (itemOn >= playback_resume && itemOn <= playback_advanceframe)
itemOn -= playback_resume - playback_pause;
}
if (modeattacking)
@ -173,10 +173,10 @@ static void M_PlaybackTick(void)
for (i = playback_viewcount; i <= playback_director; i++)
PAUSE_PlaybackMenu[i].status = IT_DISABLED;
PAUSE_PlaybackMenu[playback_freecam].mvar1 = 72;
PAUSE_PlaybackMenu[playback_quit].mvar1 = 88;
PAUSE_PlaybackMenu[playback_freecam].mvar1 = 88;
PAUSE_PlaybackMenu[playback_quit].mvar1 = 104;
currentMenu->x = BASEVIDWIDTH/2 - 52;
currentMenu->x = BASEVIDWIDTH/2 - 60;
}
else
{
@ -189,11 +189,11 @@ static void M_PlaybackTick(void)
for (i = r_splitscreen+1; i < 4; i++)
PAUSE_PlaybackMenu[playback_view1+i].status = IT_DISABLED;
PAUSE_PlaybackMenu[playback_freecam].mvar1 = 172;
PAUSE_PlaybackMenu[playback_quit].mvar1 = 188;
PAUSE_PlaybackMenu[playback_freecam].mvar1 = 188;
PAUSE_PlaybackMenu[playback_quit].mvar1 = 204;
//currentMenu->x = BASEVIDWIDTH/2 - 94;
currentMenu->x = BASEVIDWIDTH/2 - 96;
//currentMenu->x = BASEVIDWIDTH/2 - 102;
currentMenu->x = BASEVIDWIDTH/2 - 104;
}
}
@ -204,34 +204,38 @@ void M_SetPlaybackMenuPointer(void)
void M_PlaybackRewind(INT32 choice)
{
#if 0
static tic_t lastconfirmtime;
const tic_t curleveltime = leveltime;
(void)choice;
if (!demo.rewinding)
if (choice == playback_rewind)
{
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);
paused = true;
S_PauseAudio();
g_fast_forward = curleveltime - (5 * TICRATE);
g_fast_forward_clock_stop = INFTICS; //I_GetTime() + 2 * TICRATE; -- maybe?
}
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();
G_ConfirmRewind(leveltime);
M_ClearMenus(true);
}
CV_SetValue(&cv_playbackspeed, 1);
#else
(void)choice;
G_DoPlayDemo(NULL); // Restart the current demo
M_ClearMenus(true);
#endif
}
void M_PlaybackPause(INT32 choice)
@ -240,13 +244,7 @@ void M_PlaybackPause(INT32 choice)
paused = !paused;
if (demo.rewinding)
{
G_ConfirmRewind(leveltime);
paused = true;
S_PauseAudio();
}
else if (paused)
if (paused)
S_PauseAudio();
else
S_ResumeAudio();
@ -258,12 +256,6 @@ void M_PlaybackFastForward(INT32 choice)
{
(void)choice;
if (demo.rewinding)
{
G_ConfirmRewind(leveltime);
paused = false;
S_ResumeAudio();
}
CV_SetValue(&cv_playbackspeed, cv_playbackspeed.value == 1 ? 4 : 1);
}

View file

@ -8388,6 +8388,46 @@ void P_LoadLevelMusic(void)
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.
*
* \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.
// Map header should always be in place at this point
INT32 i, ranspecialwipe = 0;
sector_t *ss;
virtlump_t *encoreLump = NULL;
levelloading = true;
@ -8474,7 +8513,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
wipegamestate = gamestate; // Don't fade if reloading the gamestate
// Encore mode fade to pink to white
// 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)
{
@ -8545,7 +8584,14 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
// Let's fade to white here
// 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;
@ -8558,15 +8604,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE));
#endif
if (demo.attract)
{
; // 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())
if (K_PodiumSequence())
{
// mapmusrng is set by local player position in K_ResetCeremony
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);
// 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);
}
}
}
/*
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);
P_FreeLevelState();
R_InitializeLevelInterpolators();

View file

@ -107,6 +107,7 @@ extern mapthing_t *mapthings;
void P_SetupLevelSky(const char *skytexname, boolean global);
void P_RespawnThings(void);
void P_FreeLevelState(void);
void P_ResetLevelMusic(void);
boolean P_UseContinuousLevelMusic(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
if (paused || P_AutoPause())
{
if (demo.rewinding && leveltime > 0)
{
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!
P_RunChaseCameras(); // special case: allow freecam to MOVE during pause!
return;
}
@ -762,17 +754,23 @@ void P_Ticker(boolean run)
if (demo.recording)
{
G_WriteDemoExtraData();
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
G_WriteDemoTiccmd(&players[i].cmd, i);
if (!G_ConsiderEndingDemoWrite())
{
G_WriteDemoExtraData();
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
G_WriteDemoTiccmd(&players[i].cmd, i);
}
}
if (demo.playback && !demo.waitingfortally)
{
G_ReadDemoExtraData();
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
G_ReadDemoTiccmd(&players[i].cmd, i);
if (!G_ConsiderEndingDemoRead())
{
G_ReadDemoExtraData();
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
G_ReadDemoTiccmd(&players[i].cmd, i);
}
}
LUA_ResetTicTimers();
@ -1078,7 +1076,7 @@ void P_Ticker(boolean run)
}
}
if (g_fast_forward == 0)
if (g_fast_forward == 0 || demo.simplerewind)
{
timeinmap++;
}
@ -1168,14 +1166,21 @@ void P_Ticker(boolean run)
if (demo.recording)
{
G_WriteAllGhostTics();
if (cv_recordmultiplayerdemos.value && demo.savebutton && demo.savebutton + 3*TICRATE < leveltime)
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)
@ -1238,9 +1243,6 @@ void P_Ticker(boolean run)
P_MapEnd();
if (demo.playback)
G_StoreRewindInfo();
for (i = 0; i < MAXPLAYERS; i++)
{
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_endtime = 4*TICRATE; // + (10*NEWTICRATERATIO);
lt_fade = 0;
WipeStageTitle = false;
}
//
@ -788,7 +790,7 @@ patch_t *ST_getRoundPicture(boolean small)
//
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;
boolean doroundicon = (ST_getRoundPicture(false) != NULL);

View file

@ -76,7 +76,6 @@ TYPEDEF (plrconfig);
TYPEDEF (filesneededconfig_pak);
TYPEDEF (doomdata_t);
TYPEDEF (serverelem_t);
TYPEDEF (rewind_t);
TYPEDEF (clientkey_pak);
TYPEDEF (serverchallenge_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.
// 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)
{
return;
if (!adminmode
|| menuqueue.size == 0)
{
return;
}
}
// 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_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;
fixed_t playerx = 0, playery = 0;
UINT8 pskin = MAXSKINS;
@ -1079,10 +1087,37 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
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 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
// progression bar on the last non-rankrestricted round.
if (standings->showrank == true)
if (!adminmode && standings->showrank == true)
{
fixed_t percentslide = 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);
}
// Draw to left side of screen
while (xiter > -bufferspace)
{
xiter -= 24;
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++)
{
// 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
{
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
{
// 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
// (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;
@ -1342,7 +1385,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
}
else if (
doanimations == true
&& roundqueue.position == roundqueue.size-1
&& roundqueue.position == workingqueuesize
&& timer - interpoffs <= 2*TICRATE
)
{
@ -1522,13 +1565,62 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
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!
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
playerx -= (10 * FRACUNIT);
playery -= (14 * FRACUNIT);
@ -1934,7 +2026,7 @@ skiptallydrawer:
goto finalcounter;
// 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)
{

View file

@ -54,7 +54,7 @@ void Y_Ticker(void);
// Specific sub-drawers
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_DrawRankMode(INT32 x, INT32 y, boolean center);