From 190b0d466083011356864ad125effd970ac488c3 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Mon, 28 Oct 2019 12:14:51 -0400 Subject: [PATCH] Replay playback changes: - Store level state periodically - Load the closest usable level state when done rewinding for quicker resumes - Make playback menu fade out after 5 seconds of no activity (but not while paused) - Remove wrapping from replay hut (original wrapping was broken at some point before 1.1 and it's not necessary anyway) - Allow holding enter on frame advance for noisy slow-mo from fickleheart --- src/d_clisrv.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/d_clisrv.h | 15 +++++++++++++ src/g_game.c | 41 +++++++++++++++++++++++++++++------ src/m_menu.c | 31 ++++++++++++++++++--------- 4 files changed, 129 insertions(+), 16 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 213f5dde..49738ddb 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5567,3 +5567,61 @@ 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) +{ + rewind_t *rewind; + + if (rewindhead && rewindhead->leveltime + REWIND_POINT_INTERVAL > leveltime) + return NULL; + + rewind = (rewind_t *)malloc(sizeof (rewind_t)); + if (!rewind) + return NULL; + + CONS_Printf("Saving rewind point for time %d\n", leveltime); + save_p = rewind->savebuffer; + P_SaveNetGame(); + rewind->leveltime = leveltime; + rewind->next = rewindhead; + rewind->demopos = demopos; + rewindhead = rewind; + + return rewind; +} + +rewind_t *CL_RewindToTime(tic_t time) +{ + rewind_t *rewind; + + while (rewindhead && rewindhead->leveltime > time) + { + rewind = rewindhead->next; + free(rewindhead); + rewindhead = rewind; + } + + if (!rewindhead) + return NULL; + + CONS_Printf("Restoring rewind to time %d for goal time %d\n", rewindhead->leveltime, time); + save_p = rewindhead->savebuffer; + P_LoadNetGame(); + wipegamestate = gamestate; // No fading back in! + timeinmap = leveltime; + + return rewindhead; +} diff --git a/src/d_clisrv.h b/src/d_clisrv.h index e961c38f..3c089374 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -604,4 +604,19 @@ UINT8 GetFreeXCmdSize(void); extern UINT8 hu_resynching; extern UINT8 hu_stopped; // kart, true when the game is stopped for players due to a disconnecting or connecting player + +typedef struct rewind_s { + UINT8 savebuffer[(768*1024)]; + tic_t leveltime; + size_t demopos; + + ticcmd_t oldcmd[MAXPLAYERS]; + mobj_t oldghost[MAXPLAYERS]; + + struct rewind_s *next; +} rewind_t; + +void CL_ClearRewinds(void); +rewind_t *CL_SaveRewindPoint(size_t demopos); +rewind_t *CL_RewindToTime(tic_t time); #endif diff --git a/src/g_game.c b/src/g_game.c index b8a1a3bf..93e7e4fe 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4863,6 +4863,16 @@ void G_ReadDemoExtraData(void) INT32 p, extradata, i; char name[17]; + if (leveltime > starttime) + { + rewind_t *rewind = CL_SaveRewindPoint(demo_p - demobuffer); + if (rewind) + { + memcpy(rewind->oldcmd, oldcmd, sizeof (oldcmd)); + memcpy(rewind->oldghost, oldghost, sizeof (oldghost)); + } + } + memset(name, '\0', 17); p = READUINT8(demo_p); @@ -5901,6 +5911,8 @@ static rewindinfo_t *rewindhead = NULL; // Reverse chronological order void G_InitDemoRewind(void) { + CL_ClearRewinds(); + while (rewindhead) { rewindinfo_t *p = rewindhead->prev; @@ -6022,19 +6034,35 @@ void G_ConfirmRewind(tic_t rewindtime) CV_StealthSetValue(&cv_renderview, 0); - if (rewindtime > starttime) + if (rewindtime <= starttime) { - sound_disabled = true; // Prevent sound spam - demo.rewinding = true; + demo.rewinding = false; + G_DoPlayDemo(NULL); // Restart the current demo } else - demo.rewinding = false; + { + rewind_t *rewind; + sound_disabled = true; // Prevent sound spam + demo.rewinding = true; - G_DoPlayDemo(NULL); // Restart the current demo + rewind = CL_RewindToTime(rewindtime); + + if (rewind) + { + demo_p = demobuffer + 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++) { - //TryRunTics(1); G_Ticker((j % NEWTICRATERATIO) == 0); } @@ -8093,6 +8121,7 @@ void G_StopDemo(void) CV_SetValue(&cv_playbackspeed, 1); demo.rewinding = false; + CL_ClearRewinds(); if (gamestate == GS_LEVEL && rendermode != render_none) { diff --git a/src/m_menu.c b/src/m_menu.c index 97b1ce9b..264b1864 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -516,6 +516,7 @@ static menuitem_t MISC_ReplayOptionsMenu[] = {IT_CVAR|IT_STRING, NULL, "Sync Check Interval", &cv_netdemosyncquality, 10}, }; +static tic_t playback_last_menu_interaction_leveltime = 0; static menuitem_t PlaybackMenu[] = { {IT_CALL | IT_STRING, "M_PHIDE", "Hide Menu", M_SelectableClearMenus, 0}, @@ -2785,6 +2786,7 @@ boolean M_Responder(event_t *ev) if (currentMenu == &PlaybackMenuDef) { + playback_last_menu_interaction_leveltime = leveltime; // Flip left/right with up/down for the playback menu, since it's a horizontal icon row. switch (ch) { @@ -2846,9 +2848,9 @@ boolean M_Responder(event_t *ev) if (currentMenu == &PlaybackMenuDef) { boolean held = (boolean)playback_enterheld; - playback_enterheld = TICRATE/7; if (held) return true; + playback_enterheld = 3; } if (routine) @@ -3021,6 +3023,7 @@ void M_StartControlPanel(void) if (demo.playback) { currentMenu = &PlaybackMenuDef; + playback_last_menu_interaction_leveltime = leveltime; } else if (!Playing()) { @@ -5193,6 +5196,7 @@ void M_ReplayHut(INT32 choice) G_SetGamestate(GS_TIMEATTACK); demo.rewinding = false; + CL_ClearRewinds(); S_ChangeMusicInternal("replst", true); } @@ -5205,7 +5209,8 @@ static void M_HandleReplayHutList(INT32 choice) if (dir_on[menudepthleft]) dir_on[menudepthleft]--; else - M_PrevOpt(); + return; + //M_PrevOpt(); S_StartSound(NULL, sfx_menu1); replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; @@ -5215,7 +5220,8 @@ static void M_HandleReplayHutList(INT32 choice) if (dir_on[menudepthleft] < sizedirmenu-1) dir_on[menudepthleft]++; else - itemOn = 0; // Not M_NextOpt because that would take us to the extra dummy item + return; + //itemOn = 0; // Not M_NextOpt because that would take us to the extra dummy item S_StartSound(NULL, sfx_menu1); replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; @@ -5722,6 +5728,11 @@ static void M_DrawPlaybackMenu(void) INT16 i; patch_t *icon; UINT8 *activemap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GOLD, GTC_MENUCACHE); + UINT32 transmap = max(0, (INT32)(leveltime - playback_last_menu_interaction_leveltime - 4*TICRATE)) / 5; + transmap = min(8, transmap) << V_ALPHASHIFT; + + if (leveltime - playback_last_menu_interaction_leveltime >= 6*TICRATE) + playback_last_menu_interaction_leveltime = leveltime - 6*TICRATE; // Toggle items if (paused && !demo.rewinding) @@ -5803,16 +5814,16 @@ static void M_DrawPlaybackMenu(void) icon = W_CachePatchName("PLAYRANK", PU_CACHE); // temp if ((i == playback_fastforward && cv_playbackspeed.value > 1) || (i == playback_rewind && demo.rewinding)) - V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].alphaKey, currentMenu->y, V_SNAPTOTOP, icon, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JAWZ, GTC_MENUCACHE)); + V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].alphaKey, currentMenu->y, transmap|V_SNAPTOTOP, icon, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JAWZ, GTC_MENUCACHE)); else - V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].alphaKey, currentMenu->y, V_SNAPTOTOP, icon, (i == itemOn) ? activemap : inactivemap); + V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].alphaKey, currentMenu->y, transmap|V_SNAPTOTOP, icon, (i == itemOn) ? activemap : inactivemap); if (i == itemOn) { V_DrawCharacter(currentMenu->x + currentMenu->menuitems[i].alphaKey + 4, currentMenu->y + 14, - '\x1A' | V_SNAPTOTOP|highlightflags, false); + '\x1A' | transmap|V_SNAPTOTOP|highlightflags, false); - V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 18, V_SNAPTOTOP|V_ALLOWLOWERCASE, currentMenu->menuitems[i].text); + V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 18, transmap|V_SNAPTOTOP|V_ALLOWLOWERCASE, currentMenu->menuitems[i].text); if ((currentMenu->menuitems[i].status & IT_TYPE) == IT_ARROWS) { @@ -5820,11 +5831,11 @@ static void M_DrawPlaybackMenu(void) if (!(i == playback_viewcount && splitscreen == 3)) V_DrawCharacter(BASEVIDWIDTH/2 - 4, currentMenu->y + 28 - (skullAnimCounter/5), - '\x1A' | V_SNAPTOTOP|highlightflags, false); // up arrow + '\x1A' | transmap|V_SNAPTOTOP|highlightflags, false); // up arrow if (!(i == playback_viewcount && splitscreen == 0)) V_DrawCharacter(BASEVIDWIDTH/2 - 4, currentMenu->y + 48 + (skullAnimCounter/5), - '\x1B' | V_SNAPTOTOP|highlightflags, false); // down arrow + '\x1B' | transmap|V_SNAPTOTOP|highlightflags, false); // down arrow switch (i) { @@ -5843,7 +5854,7 @@ static void M_DrawPlaybackMenu(void) continue; } - V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 38, V_SNAPTOTOP|V_ALLOWLOWERCASE|highlightflags, str); + V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 38, transmap|V_SNAPTOTOP|V_ALLOWLOWERCASE|highlightflags, str); } } }