Merge branch 'attract-fixes' into 'master'

Attract mode and Credits fixes

Closes #1053, #1166, and #1167

See merge request KartKrew/Kart!2128
This commit is contained in:
AJ Martinez 2024-03-19 00:46:53 +00:00
commit 858eecaef3
21 changed files with 232 additions and 69 deletions

View file

@ -2200,7 +2200,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
else
{
I_Sleep(cv_sleep.value);
I_UpdateTime(cv_timescale.value);
I_UpdateTime();
}
return true;

View file

@ -150,6 +150,7 @@ boolean g_singletics = false; // timedemo
boolean lastdraw = false;
tic_t g_fast_forward = 0;
tic_t g_fast_forward_clock_stop = INFTICS;
postimg_t postimgtype[MAXSPLITSCREENPLAYERS];
INT32 postimgparam[MAXSPLITSCREENPLAYERS];
@ -649,6 +650,13 @@ static bool D_Display(void)
V_DrawCustomFadeScreen("FADEMAP0", val);
}
}
else if (demo.attract == DEMO_ATTRACT_TITLE)
{
if (INT32 fade = F_AttractDemoExitFade())
{
V_DrawCustomFadeScreen("FADEMAP0", fade);
}
}
VID_DisplaySoftwareScreen();
}
@ -829,7 +837,7 @@ void D_SRB2Loop(void)
// Pushing of + parameters is now done back in D_SRB2Main, not here.
I_UpdateTime(cv_timescale.value);
I_UpdateTime();
oldentertics = I_GetTime();
// end of loading screen: CONS_Printf() will no more call FinishUpdate()
@ -877,7 +885,7 @@ void D_SRB2Loop(void)
bool ranwipe = false;
I_UpdateTime(cv_timescale.value);
I_UpdateTime();
if (lastwipetic)
{
@ -1131,12 +1139,19 @@ static boolean g_deferredtitle = false;
//
void D_StartTitle(void)
{
bool fromAttract = (demo.attract == DEMO_ATTRACT_TITLE);
demo.attract = DEMO_ATTRACT_OFF;
Music_StopAll();
D_ClearState();
F_StartTitleScreen();
M_ClearMenus(false);
g_deferredtitle = false;
if (fromAttract)
S_ShowMusicCredit(); // Show music credit when returning to the title screen
}
void D_SetDeferredStartTitle(boolean deferred)

View file

@ -600,7 +600,7 @@ void Net_WaitAllAckReceived(UINT32 timeout)
while (tictac == I_GetTime())
{
I_Sleep(cv_sleep.value);
I_UpdateTime(cv_timescale.value);
I_UpdateTime();
}
tictac = I_GetTime();
HGetPacket();

View file

@ -71,6 +71,7 @@
#include "k_roulette.h"
#include "k_bans.h"
#include "k_director.h"
#include "k_credits.h"
#ifdef SRB2_CONFIG_ENABLE_WEBM_MOVIES
#include "m_avrecorder.h"
@ -6374,14 +6375,20 @@ void Command_ExitGame_f(void)
if (!modeattacking)
{
if (restoreMenu == NULL)
// YES, this is where demo.attract gets cleared!
if (demo.attract == DEMO_ATTRACT_CREDITS)
{
D_StartTitle();
F_ContinueCredits(); // <-- clears demo.attract
}
else if (restoreMenu == NULL) // this is true for attract demos too!
{
D_StartTitle(); // <-- clears demo.attract
}
else
{
D_ClearState();
M_StartControlPanel();
demo.attract = DEMO_ATTRACT_OFF; // shouldn't ever happen, but let's keep the code symmetrical
}
}
}

View file

@ -927,6 +927,7 @@ extern INT16 wipetypepost;
// debug flag to cancel adaptiveness
extern boolean g_singletics;
extern tic_t g_fast_forward;
extern tic_t g_fast_forward_clock_stop;
#define singletics (g_singletics == true || g_fast_forward > 0)

View file

@ -65,6 +65,10 @@ static tic_t stoptimer;
static boolean keypressed = false;
static tic_t attractcountdown; // Countdown until attract demo ends
static boolean attractcredit; // Show music credit once attract demo begins
boolean g_attractnowipe; // Do not wipe on return to title screen
static INT32 menuanimtimer; // Title screen: background animation timing
altview_t titlemapcam = {0};
@ -1747,7 +1751,7 @@ void F_TitleScreenTicker(boolean run)
UINT16 mapnum;
UINT8 numstaff;
static boolean use_netreplay = false;
staffbrief_t *brief;
staffbrief_t *brief = NULL;
if ((use_netreplay = !use_netreplay))
{
@ -1785,6 +1789,40 @@ loadreplay:
demo.attract = DEMO_ATTRACT_TITLE;
demo.ignorefiles = true;
demo.loadfiles = false;
attractcountdown = INFTICS;
if (brief)
{
// "Random" table of times to skip forward in the demo.
// I didn't want to use real random functions because I didn't like the distribution.
tic_t table[] = {
0,
15*TICRATE,
brief->lap / 2, // references to brief->lap will skip to the end of Prison replays
0,
40*TICRATE,
brief->lap,
0,
0,
brief->time,
};
UINT8 numintable = sizeof table / sizeof *table;
static UINT8 index = UINT8_MAX;
if (index == UINT8_MAX)
index = M_RandomKey(numintable);
else
index = (index + 1) % numintable;
attractcountdown = min(30*TICRATE, brief->time);
g_fast_forward = min(table[index], brief->time - attractcountdown);
// Slow computers, don't wait all day
g_fast_forward_clock_stop = I_GetTime() + 2*TICRATE;
// Show title screen music credit at beginning of demo
attractcredit = true;
}
G_DoPlayDemoEx(dname, dlump);
}
}
@ -1792,6 +1830,30 @@ loadreplay:
void F_AttractDemoTicker(void)
{
keypressed = false;
if (attractcountdown > 0 && !g_fast_forward)
{
if (attractcredit)
{
S_ShowMusicCredit();
attractcredit = false;
}
if (attractcountdown > 0 && !--attractcountdown)
{
// Fade will be handled without a wipe (see F_AttractDemoExitFade)
g_attractnowipe = true;
G_CheckDemoStatus();
}
}
}
INT32 F_AttractDemoExitFade(void)
{
if (attractcountdown > 15)
return 0;
return 31 - (attractcountdown * 2);
}
// ================

View file

@ -64,6 +64,8 @@ void F_EndTextPrompt(boolean forceexec, boolean noexec);
boolean F_GetPromptHideHudAll(void);
boolean F_GetPromptHideHud(fixed_t y);
INT32 F_AttractDemoExitFade(void);
void F_StartGameEnd(void);
void F_StartIntro(void);
void F_StartTitleScreen(void);
@ -128,6 +130,8 @@ extern boolean WipeStageTitle;
extern INT32 lastwipetic;
extern boolean g_attractnowipe;
// Don't know where else to place this constant
// But this file seems appropriate
#define PRELEVELTIME TICRATE // frames in tics

View file

@ -507,7 +507,7 @@ void F_RunWipe(UINT8 wipemode, UINT8 wipetype, boolean drawMenu, const char *col
while (!((nowtime = I_GetTime()) - lastwipetic))
{
I_Sleep(cv_sleep.value);
I_UpdateTime(cv_timescale.value);
I_UpdateTime();
}
lastwipetic = nowtime;

View file

@ -1285,7 +1285,7 @@ void G_PreLevelTitleCard(void)
while (!((nowtime = I_GetTime()) - lasttime))
{
I_Sleep(cv_sleep.value);
I_UpdateTime(cv_timescale.value);
I_UpdateTime();
}
lasttime = nowtime;
}
@ -1348,7 +1348,6 @@ boolean G_Responder(event_t *ev)
{
// stop the title demo
G_CheckDemoStatus();
demo.attract = DEMO_ATTRACT_OFF;
return true;
}
}
@ -2031,7 +2030,19 @@ void G_Ticker(boolean run)
if (g_fast_forward > 0)
{
if (I_GetTime() > g_fast_forward_clock_stop)
{
// If too much real time has passed, end the fast-forward early.
g_fast_forward = 1;
}
g_fast_forward--;
if (g_fast_forward == 0)
{
// Next fast-forward is unlimited.
g_fast_forward_clock_stop = INFTICS;
}
}
}
}

View file

@ -18,6 +18,8 @@
#include "command.h"
#include "doomtype.h"
#include "d_netcmd.h"
#include "f_finale.h"
#include "g_demo.h"
#include "m_fixed.h"
#include "i_system.h"
@ -57,14 +59,25 @@ void I_InitializeTime(void)
I_StartupTimer();
}
void I_UpdateTime(fixed_t timescale)
fixed_t I_GetTimeScale(void)
{
if (demo.playback && demo.attract == DEMO_ATTRACT_TITLE && F_AttractDemoExitFade())
{
// Slow down at the end of attract demos
return FRACUNIT/2;
}
return cv_timescale.value;
}
void I_UpdateTime(void)
{
double ticratescaled;
double elapsedseconds;
tic_t realtics;
// get real tics
ticratescaled = (double)TICRATE * FIXED_TO_FLOAT(timescale);
ticratescaled = (double)TICRATE * FIXED_TO_FLOAT(I_GetTimeScale());
enterprecise = I_GetPreciseTime();
elapsedseconds = (double)(enterprecise - oldenterprecise) / I_GetPrecisePrecision();

View file

@ -38,7 +38,8 @@ tic_t I_GetTime(void);
*/
void I_InitializeTime(void);
void I_UpdateTime(fixed_t timescale);
void I_UpdateTime(void);
fixed_t I_GetTimeScale(void);
/** \brief Block for at minimum the duration specified. This function makes a
best effort not to oversleep, and will spinloop if sleeping would

View file

@ -430,6 +430,7 @@ void F_ContinueCredits(void)
{
G_SetGamestate(GS_CREDITS);
F_CreditsReset();
demo.attract = DEMO_ATTRACT_OFF;
// Returning from playing a demo.
// Go to the next slide.
@ -524,6 +525,8 @@ static boolean F_CreditsPlayDemo(void)
G_DoPlayDemoEx("", (brief->wad << 16) | brief->lump);
g_fast_forward = 30 * TICRATE;
// Slow computers, don't wait all day
g_fast_forward_clock_stop = I_GetTime() + 2 * TICRATE;
g_credits.demo_exit = 0;
return true;
}

View file

@ -52,6 +52,7 @@
#include "k_hitlag.h"
#include "g_input.h"
#include "k_dialogue.h"
#include "f_finale.h"
//{ Patch Definitions
static patch_t *kp_nodraw;
@ -6097,6 +6098,33 @@ void K_drawKartHUD(void)
{
INT32 x = BASEVIDWIDTH - 8, y = BASEVIDHEIGHT-8, snapflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SLIDEIN;
patch_t *pat = static_cast<patch_t*>(W_CachePatchName((M_UseAlternateTitleScreen() ? "MTSJUMPR1" : "MTSBUMPR1"), PU_CACHE));
const UINT8 *colormap = nullptr;
if (INT32 fade = F_AttractDemoExitFade())
{
// TODO: Twodee cannot handle
// V_DrawCustomFadeScreen.
// However, since the screen fade just
// uses a colormap, the same colormap can
// be applied on a per-patch basis.
// I'm only bothering to apply this
// colormap to the attract mode sticker,
// since it's the lone HUD element.
if (lighttable_t *clm = V_LoadCustomFadeMap("FADEMAP0"))
{
// This must be statically allocated for Twodee
static UINT8 *colormap_storage;
const UINT8 *fadetable = V_OffsetIntoFadeMap(clm, fade);
if (!colormap_storage)
Z_MallocAlign(256, PU_STATIC, &colormap_storage, 8);
memcpy(colormap_storage, fadetable, 256);
colormap = colormap_storage;
Z_Free(clm);
}
}
if (r_splitscreen == 3)
{
@ -6105,7 +6133,7 @@ void K_drawKartHUD(void)
snapflags = 0;
}
V_DrawScaledPatch(x-(SHORT(pat->width)), y-(SHORT(pat->height)), snapflags, pat);
V_DrawMappedPatch(x-(SHORT(pat->width)), y-(SHORT(pat->height)), snapflags, pat, colormap);
}
}
else

View file

@ -1417,7 +1417,9 @@ void K_TickPlayerTally(player_t *player)
&& (!netgame && !demo.playback)
&& player->tally.state != TALLY_ST_DONE;
if (fastForwardInput && allowFastForward)
if ((fastForwardInput && allowFastForward) ||
// Skip tally in atract demos
(demo.playback && demo.attract))
{
do
player->tally.Tick();

View file

@ -134,7 +134,7 @@ void M_QuitResponse(INT32 ch)
V_DrawSmallScaledPatch(0, 0, 0, W_CachePatchName("GAMEQUIT", PU_CACHE)); // Demo 3 Quit Screen Tails 06-16-2001
I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001
I_Sleep(cv_sleep.value);
I_UpdateTime(cv_timescale.value);
I_UpdateTime();
}
}
I_Quit();

View file

@ -94,8 +94,12 @@ void M_EndModeAttackRun(void)
// - Now we need to clear the rest of the gamestate ourself!
}
// Playback: modeattacking is always false, so calling this returns to the menu.
// Recording: modeattacking is still true and this function call preserves that.
// Playback:
// - modeattacking is always false, so calling this returns to the menu.
// - Because modeattacking is false, also clears demo.attract.
//
// Recording:
// - modeattacking is still true and this function call preserves that.
Command_ExitGame_f();
if (!modeattacking)
@ -123,19 +127,8 @@ void M_EndModeAttackRun(void)
modeattacking = ATTACKING_NONE;
// Return to the menu.
if (demo.attract == DEMO_ATTRACT_TITLE)
{
D_SetDeferredStartTitle(true);
}
else if (demo.attract == DEMO_ATTRACT_CREDITS)
{
F_ContinueCredits();
}
else
{
D_ClearState();
M_StartControlPanel();
}
D_ClearState();
M_StartControlPanel();
}
// Replay Playback Menu

View file

@ -8342,8 +8342,10 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
P_InitLevelSettings();
if (demo.attract != DEMO_ATTRACT_TITLE)
if (demo.attract != DEMO_ATTRACT_TITLE && gamestate != GS_TITLESCREEN)
{
// Stop titlescreen music from overriding level music.
// Except on the title screen, where an attract demo or title map may be used.
Music_Stop("title");
}
@ -8391,7 +8393,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
while (!((nowtime = I_GetTime()) - lastwipetic)) \
{ \
I_Sleep(cv_sleep.value); \
I_UpdateTime(cv_timescale.value); \
I_UpdateTime(); \
} \
lastwipetic = nowtime; \
if (moviemode && rendermode == render_opengl) \
@ -8530,15 +8532,24 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
wipetype = wipe_encore_towhite;
}
if (rendermode != render_none)
if (g_attractnowipe)
{
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
F_WipeEndScreen();
// Attract demos do a custom fade on exit, so
// don't run a wipe here.
g_attractnowipe = false;
}
else
{
if (rendermode != render_none)
{
F_WipeStartScreen();
F_RunWipe(wipetype, wipedefs[wipetype], false, ((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), false, false);
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
F_WipeEndScreen();
}
F_RunWipe(wipetype, wipedefs[wipetype], false, ((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), false, false);
}
}
/*
@ -8856,11 +8867,6 @@ void P_PostLoadLevel(void)
G_BeginRecording(); // I AM NOW READY TO RECORD.
demo.deferstart = true;
if (demo.attract == DEMO_ATTRACT_TITLE)
{
S_ShowMusicCredit();
}
nextmapoverride = 0;
skipstats = 0;

View file

@ -50,7 +50,7 @@ UINT32 R_GetFramerateCap(void)
boolean R_UsingFrameInterpolation(void)
{
return (R_GetFramerateCap() != TICRATE || cv_timescale.value < FRACUNIT);
return (R_GetFramerateCap() != TICRATE || I_GetTimeScale() < FRACUNIT);
}
static viewvars_t pview_old[MAXSPLITSCREENPLAYERS];

View file

@ -1566,6 +1566,31 @@ void V_DrawFadeScreen(UINT16 color, UINT8 strength)
.done();
}
lighttable_t *V_LoadCustomFadeMap(const char *lump)
{
lumpnum_t lumpnum = LUMPERROR;
lighttable_t *clm = NULL;
if (lump != NULL)
lumpnum = W_GetNumForName(lump);
else
return NULL;
if (lumpnum != LUMPERROR)
{
clm = static_cast<lighttable_t*>(Z_MallocAlign(COLORMAP_SIZE, PU_STATIC, NULL, 8));
W_ReadLump(lumpnum, clm);
return clm;
}
return NULL;
}
const UINT8 *V_OffsetIntoFadeMap(const lighttable_t *clm, UINT8 strength)
{
return ((const UINT8 *)clm + strength*256);
}
//
// Fade the screen buffer, using a custom COLORMAP lump.
// Split from V_DrawFadeScreen, because that function has
@ -1583,33 +1608,21 @@ void V_DrawCustomFadeScreen(const char *lump, UINT8 strength)
// TODO: fix this for Twodee
{
lumpnum_t lumpnum = LUMPERROR;
lighttable_t *clm = NULL;
lighttable_t *clm = V_LoadCustomFadeMap(lump);
if (lump != NULL)
lumpnum = W_GetNumForName(lump);
else
return;
if (lumpnum != LUMPERROR)
if (clm != NULL)
{
clm = static_cast<lighttable_t*>(Z_MallocAlign(COLORMAP_SIZE, PU_STATIC, NULL, 8));
W_ReadLump(lumpnum, clm);
const UINT8 *fadetable = V_OffsetIntoFadeMap(clm, strength);
const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
UINT8 *buf = screens[0];
if (clm != NULL)
{
const UINT8 *fadetable = ((UINT8 *)clm + strength*256);
const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
UINT8 *buf = screens[0];
// heavily simplified -- we don't need to know x or y
// position when we're doing a full screen fade
for (; buf < deststop; ++buf)
*buf = fadetable[*buf];
// heavily simplified -- we don't need to know x or y
// position when we're doing a full screen fade
for (; buf < deststop; ++buf)
*buf = fadetable[*buf];
Z_Free(clm);
clm = NULL;
}
Z_Free(clm);
clm = NULL;
}
}
}

View file

@ -236,6 +236,8 @@ void V_DrawFadeScreen(UINT16 color, UINT8 strength);
// available to lua over my dead body, which will probably happen in this heat
void V_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c, UINT16 color, UINT8 strength);
lighttable_t *V_LoadCustomFadeMap(const char *lump);
const UINT8 *V_OffsetIntoFadeMap(const lighttable_t *clm, UINT8 strength);
void V_DrawCustomFadeScreen(const char *lump, UINT8 strength);
void V_DrawFadeConsBack(INT32 plines);

View file

@ -1951,7 +1951,9 @@ void Y_DetermineIntermissionType(void)
// or for explicit requested skip (outside of modeattacking)
|| (modeattacking == ATTACKING_NONE && skipstats != 0)
// or tutorial skip material
|| (nextmapoverride == NEXTMAP_TUTORIALCHALLENGE+1 || tutorialchallenge != TUTORIALSKIP_NONE))
|| (nextmapoverride == NEXTMAP_TUTORIALCHALLENGE+1 || tutorialchallenge != TUTORIALSKIP_NONE)
// or title screen attract demos
|| (demo.playback && demo.attract == DEMO_ATTRACT_TITLE))
{
intertype = int_none;
return;