Skip to content
Snippets Groups Projects
d_main.c 41.8 KiB
Newer Older
Alam Ed Arias's avatar
Alam Ed Arias committed
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2018 by Sonic Team Junior.
Alam Ed Arias's avatar
Alam Ed Arias committed
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file  d_main.c
/// \brief SRB2 main program
///
///        SRB2 main program (D_SRB2Main) and game loop (D_SRB2Loop),
///        plus functions to parse command line parameters, configure game
///        parameters, and call the startup functions.

#if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON)
#include <sys/stat.h>
#include <sys/types.h>
#endif

#ifdef __GNUC__
#include <unistd.h> // for getcwd
#endif

#ifdef PC_DOS
#include <stdio.h> // for snprintf
int	snprintf(char *str, size_t n, const char *fmt, ...);
//int	vsnprintf(char *str, size_t n, const char *fmt, va_list ap);
#endif

#if (defined (_WIN32) && !defined (_WIN32_WCE)) && !defined (_XBOX)
#include <direct.h>
#include <malloc.h>
#endif

#if !defined (UNDER_CE)
#include <time.h>
#elif defined (_XBOX)
#define NO_TIME
#endif

#include "doomdef.h"
#include "am_map.h"
#include "console.h"
#include "d_net.h"
#include "f_finale.h"
#include "g_game.h"
#include "hu_stuff.h"
#include "i_sound.h"
#include "i_system.h"
Eidolon's avatar
Eidolon committed
#include "i_time.h"
#include "i_threads.h"
Alam Ed Arias's avatar
Alam Ed Arias committed
#include "i_video.h"
#include "m_argv.h"
#include "m_menu.h"
#include "m_misc.h"
#include "p_setup.h"
#include "p_saveg.h"
#include "r_main.h"
#include "r_local.h"
#include "s_sound.h"
#include "st_stuff.h"
#include "v_video.h"
#include "w_wad.h"
#include "z_zone.h"
#include "d_main.h"
#include "d_netfil.h"
#include "m_cheat.h"
#include "y_inter.h"
#include "p_local.h" // chasecam
#include "m_misc.h" // screenshot functionality
#include "dehacked.h" // Dehacked list test
#include "m_cond.h" // condition initialization
#include "fastcmp.h"
Eidolon's avatar
Eidolon committed
#include "r_fps.h" // Frame interpolation/uncapped
#include "keys.h"
#include "filesrch.h" // refreshdirmenu
Alam Ed Arias's avatar
Alam Ed Arias committed

#ifdef CMAKECONFIG
#include "config.h"
#else
#include "config.h.in"
#endif

Alam Ed Arias's avatar
Alam Ed Arias committed
#ifdef _XBOX
#include "sdl12/SRB2XBOX/xboxhelp.h"
Alam Ed Arias's avatar
Alam Ed Arias committed
#endif

#ifdef HWRENDER
#include "hardware/hw_main.h" // 3D View Rendering
#endif

#ifdef _WINDOWS
#include "win32/win_main.h" // I_DoStartupMouse
#endif

#ifdef HW3SOUND
#include "hardware/hw3sound.h"
#endif

#ifdef HAVE_BLUA
#include "lua_script.h"
#endif

#ifdef HAVE_DISCORDRPC
#include "discord.h"
#endif

Alam Ed Arias's avatar
Alam Ed Arias committed
// platform independant focus loss
UINT8 window_notinfocus = false;

Alam Ed Arias's avatar
Alam Ed Arias committed
//
// DEMO LOOP
//
//static INT32 demosequence;
static const char *pagename = "MAP1PIC";
static char *startupwadfiles[MAX_WADFILES];
static char *startuppwads[MAX_WADFILES];
Alam Ed Arias's avatar
Alam Ed Arias committed

boolean devparm = false; // started game with -devparm

boolean singletics = false; // timedemo
boolean lastdraw = false;

Sal's avatar
Sal committed
postimg_t postimgtype[MAXSPLITSCREENPLAYERS];
INT32 postimgparam[MAXSPLITSCREENPLAYERS];
Alam Ed Arias's avatar
Alam Ed Arias committed

Marco Z's avatar
Marco Z committed
// These variables are only true if
// whether the respective sound system is disabled
// or they're init'ed, but the player just toggled them
Alam Ed Arias's avatar
Alam Ed Arias committed
#ifdef _XBOX
#ifndef NO_MIDI
boolean midi_disabled = true;
#endif
boolean sound_disabled = true;
Alam Ed Arias's avatar
Alam Ed Arias committed
#else
#ifndef NO_MIDI
Alam Ed Arias's avatar
Alam Ed Arias committed
#endif
boolean sound_disabled = false;
boolean digital_disabled = false;
Marco Z's avatar
Marco Z committed
#endif
Alam Ed Arias's avatar
Alam Ed Arias committed

#ifdef DEBUGFILE
INT32 debugload = 0;
#endif

Alam Ed Arias's avatar
Alam Ed Arias committed
#ifdef _arch_dreamcast
char srb2home[256] = "/cd";
char srb2path[256] = "/cd";
#else
char srb2home[256] = ".";
char srb2path[256] = ".";
#endif
boolean usehome = true;
const char *pandf = "%s" PATHSEP "%s";

//
// EVENT HANDLING
//
// Events are asynchronous inputs generally generated by the game user.
// Events can be discarded if no responder claims them
// referenced from i_system.c for I_GetKey()

event_t events[MAXEVENTS];
INT32 eventhead, eventtail;

boolean dedicated = false;

//
// D_PostEvent
// Called by the I/O functions when input is detected
//
void D_PostEvent(const event_t *ev)
{
	events[eventhead] = *ev;
	eventhead = (eventhead+1) & (MAXEVENTS-1);
}
// just for lock this function
#if defined (PC_DOS) && !defined (DOXYGEN)
Alam Ed Arias's avatar
Alam Ed Arias committed
void D_PostEvent_end(void) {};
#endif

// modifier keys
// Now handled in I_OsPolling
UINT8 shiftdown = 0; // 0x1 left, 0x2 right
UINT8 ctrldown = 0; // 0x1 left, 0x2 right
UINT8 altdown = 0; // 0x1 left, 0x2 right
boolean capslock = 0;	// gee i wonder what this does.
Alam Ed Arias's avatar
Alam Ed Arias committed
//
// D_ProcessEvents
// Send all the events of the given timestamp down the responder chain
//
void D_ProcessEvents(void)
{
	event_t *ev;

Alam Ed Arias's avatar
Alam Ed Arias committed
	for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
	{
		ev = &events[eventtail];

		// Screenshots over everything so that they can be taken anywhere.
		if (M_ScreenshotResponder(ev))
			continue; // ate the event

		if (gameaction == ga_nothing && gamestate == GS_TITLESCREEN)
		{
			if (cht_Responder(ev))
				continue;
		}

		if (demo.savemode == DSM_TITLEENTRY)
		{
			if (G_DemoTitleResponder(ev))
				continue;
		}

Alam Ed Arias's avatar
Alam Ed Arias committed
		// Menu input
#ifdef HAVE_THREADS
		I_lock_mutex(&m_menu_mutex);
#endif
		{
			eaten = M_Responder(ev);
		}
#ifdef HAVE_THREADS
		I_unlock_mutex(m_menu_mutex);
#endif

		if (eaten)
Alam Ed Arias's avatar
Alam Ed Arias committed
			continue; // menu ate the event

		// Demo input:
		if (demo.playback)
			if (M_DemoResponder(ev))
				continue;	// demo ate the event

Alam Ed Arias's avatar
Alam Ed Arias committed
		// console input
#ifdef HAVE_THREADS
		I_lock_mutex(&con_mutex);
#endif
		{
			eaten = CON_Responder(ev);
		}
#ifdef HAVE_THREADS
		I_unlock_mutex(con_mutex);
#endif

		if (eaten)
Alam Ed Arias's avatar
Alam Ed Arias committed
			continue; // ate the event

		G_Responder(ev);
	}
}

//
// D_Display
// draw current display, possibly wiping it from the previous
//

// wipegamestate can be set to -1 to force a wipe on the next draw
// added comment : there is a wipe eatch change of the gamestate
gamestate_t wipegamestate = GS_LEVEL;

static void D_Display(void)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	boolean forcerefresh = false;
Alam Ed Arias's avatar
Alam Ed Arias committed
	static boolean wipe = false;
	INT32 wipedefindex = 0;
Sal's avatar
Sal committed
	UINT8 i;
Alam Ed Arias's avatar
Alam Ed Arias committed

	if (!dedicated)
	{
		if (nodrawers)
			return; // for comparative timing/profiling
Alam Ed Arias's avatar
Alam Ed Arias committed

wolfs's avatar
wolfs committed
		// check for change of screen size (video mode)
		if (setmodeneeded && !wipe)
			SCR_SetMode(); // change video mode
Alam Ed Arias's avatar
Alam Ed Arias committed

		if (vid.recalc)
			SCR_Recalc(); // NOTE! setsizeneeded is set by SCR_Recalc()
Alam Ed Arias's avatar
Alam Ed Arias committed

		// change the view size if needed
		if (setsizeneeded)
		{
			R_ExecuteSetViewSize();
			forcerefresh = true; // force background redraw
		}
Alam Ed Arias's avatar
Alam Ed Arias committed

		// draw buffered stuff to screen
		// Used only by linux GGI version
		I_UpdateNoBlit();
Alam Ed Arias's avatar
Alam Ed Arias committed
	}

	// save the current screen if about to wipe
	wipe = (gamestate != wipegamestate);
Alam Ed Arias's avatar
Alam Ed Arias committed
	if (wipe)
	{
		// set for all later
		wipedefindex = gamestate; // wipe_xxx_toblack
		if (gamestate == GS_TITLESCREEN && wipegamestate != GS_INTRO)
			wipedefindex = wipe_timeattack_toblack;
		else if (gamestate == GS_INTERMISSION)
Alam Ed Arias's avatar
Alam Ed Arias committed
		{
			if (intertype == int_spec) // Special Stage
				wipedefindex = wipe_specinter_toblack;
			else //if (intertype != int_coop) // Multiplayer
Alam Ed Arias's avatar
Alam Ed Arias committed
				wipedefindex = wipe_multinter_toblack;
		}

Alam Ed Arias's avatar
Alam Ed Arias committed
		{
			// Fade to black first
			if (gamestate != GS_LEVEL // fades to black on its own timing, always
			 && wipedefs[wipedefindex] != UINT8_MAX)
			{
				F_WipeStartScreen();
Alam Ed Arias's avatar
Alam Ed Arias committed
				V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
				F_WipeEndScreen();
				F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK);
SteelT's avatar
SteelT committed
			if (gamestate != GS_LEVEL && rendermode != render_none)
			{
				V_SetPaletteLump("PLAYPAL"); // Reset the palette
				R_ReInitColormaps(0, LUMPERROR);
Alam Ed Arias's avatar
Alam Ed Arias committed
			}

			F_WipeStartScreen();
		}
		else //dedicated servers
		{
			F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK);
			wipegamestate = gamestate;
		}
Alam Ed Arias's avatar
Alam Ed Arias committed
	}

	if (dedicated) //bail out after wipe logic
Alam Ed Arias's avatar
Alam Ed Arias committed
	// do buffered drawing
	switch (gamestate)
	{
		case GS_LEVEL:
			if (!gametic)
				break;
			HU_Erase();
Lactozilla's avatar
Lactozilla committed
			AM_Drawer();
Alam Ed Arias's avatar
Alam Ed Arias committed
			break;

		case GS_INTERMISSION:
			Y_IntermissionDrawer();
			HU_Erase();
			HU_Drawer();
			break;

Sal's avatar
Sal committed
		case GS_VOTING:
			Y_VoteDrawer();
			HU_Erase();
			HU_Drawer();
			break;

Alam Ed Arias's avatar
Alam Ed Arias committed
		case GS_TIMEATTACK:
			break;

		case GS_INTRO:
			F_IntroDrawer();
			if (wipegamestate == (gamestate_t)-1)
Alam Ed Arias's avatar
Alam Ed Arias committed
				wipe = true;
				wipedefindex = gamestate; // wipe_xxx_toblack
			}
Alam Ed Arias's avatar
Alam Ed Arias committed
			break;

		case GS_CUTSCENE:
			F_CutsceneDrawer();
			HU_Erase();
			HU_Drawer();
			break;

		case GS_GAMEEND:
			F_GameEndDrawer();
			break;

		case GS_EVALUATION:
			F_GameEvaluationDrawer();
			HU_Erase();
Alam Ed Arias's avatar
Alam Ed Arias committed
			HU_Drawer();
			break;

		case GS_CONTINUING:
			F_ContinueDrawer();
			break;

		case GS_CREDITS:
			F_CreditDrawer();
			HU_Erase();
			HU_Drawer();
			break;

		case GS_TITLESCREEN:
			F_TitleScreenDrawer();
			if (wipe)
				wipedefindex = wipe_titlescreen_toblack;
Alam Ed Arias's avatar
Alam Ed Arias committed
			break;

		case GS_WAITINGPLAYERS:
			// The clientconnect drawer is independent...
			if (netgame)
			{
				// I don't think HOM from nothing drawing is independent...
				F_WaitingPlayersDrawer();
				HU_Erase();
				HU_Drawer();
			}
Alam Ed Arias's avatar
Alam Ed Arias committed
		case GS_DEDICATEDSERVER:
		case GS_NULL:
			break;
	}

	if (gamestate == GS_LEVEL)
	{
		// draw the view directly
Lactozilla's avatar
Lactozilla committed
		if (cv_renderview.value && !automapactive)
Alam Ed Arias's avatar
Alam Ed Arias committed
		{
Eidolon's avatar
Eidolon committed
			R_ApplyLevelInterpolators(R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT);

Sal's avatar
Sal committed
			for (i = 0; i <= splitscreen; i++)
Alam Ed Arias's avatar
Alam Ed Arias committed
			{
Sal's avatar
Sal committed
				if (players[displayplayers[i]].mo || players[displayplayers[i]].playerstate == PST_DEAD)
Alam Ed Arias's avatar
Alam Ed Arias committed
				{
Sal's avatar
Sal committed
					if (i == 0) // Initialize for P1
Sal's avatar
Sal committed
					{
						viewwindowy = 0;
						viewwindowx = 0;

Sal's avatar
Sal committed
						topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
						objectsdrawn = 0;
					}
Alam Ed Arias's avatar
Alam Ed Arias committed

Sal's avatar
Sal committed
					viewssnum = i;
Alam Ed Arias's avatar
Alam Ed Arias committed

Sal's avatar
Sal committed
#ifdef HWRENDER
Sal's avatar
Sal committed
					if (rendermode != render_soft)
						HWR_RenderPlayerView(i, &players[displayplayers[i]]);
					else
Sal's avatar
Sal committed
#endif
Sal's avatar
Sal committed
					if (rendermode != render_none)
					{
						if (i > 0) // Splitscreen-specific
						{
							switch (i)
Sal's avatar
Sal committed
							{
								case 1:
									if (splitscreen > 1)
									{
										viewwindowx = viewwidth;
										viewwindowy = 0;
									}
									else
									{
										viewwindowx = 0;
										viewwindowy = viewheight;
									}
									M_Memcpy(ylookup, ylookup2, viewheight*sizeof (ylookup[0]));
									break;
								case 2:
									viewwindowx = 0;
									viewwindowy = viewheight;
									M_Memcpy(ylookup, ylookup3, viewheight*sizeof (ylookup[0]));
									break;
								case 3:
									viewwindowx = viewwidth;
									viewwindowy = viewheight;
									M_Memcpy(ylookup, ylookup4, viewheight*sizeof (ylookup[0]));
								default:
									break;
							}

Sal's avatar
Sal committed
							topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
						}

						R_RenderPlayerView(&players[displayplayers[i]]);

						if (i > 0)
							M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0]));
					}
Sal's avatar
Sal committed
				}
Sal's avatar
Sal committed
			}

Sal's avatar
Sal committed
			if (rendermode == render_soft)
Sal's avatar
Sal committed
			{
Sal's avatar
Sal committed
				for (i = 0; i <= splitscreen; i++)
Sal's avatar
Sal committed
				{
Sal's avatar
Sal committed
					if (postimgtype[i])
						V_DoPostProcessor(i, postimgtype[i], postimgparam[i]);
Sal's avatar
Sal committed
				}
Sal's avatar
Sal committed
			}
Eidolon's avatar
Eidolon committed

			R_RestoreLevelInterpolators();
Alam Ed Arias's avatar
Alam Ed Arias committed
		}

		if (lastdraw)
		{
			if (rendermode == render_soft)
Alam Ed Arias's avatar
Alam Ed Arias committed
			{
Alam Ed Arias's avatar
Alam Ed Arias committed
				VID_BlitLinearScreen(screens[0], screens[1], vid.width*vid.bpp, vid.height, vid.width*vid.bpp, vid.rowbytes);
Alam Ed Arias's avatar
Alam Ed Arias committed
				usebuffer = true;
			}
Alam Ed Arias's avatar
Alam Ed Arias committed
			lastdraw = false;
		}

		ST_Drawer();
Alam Ed Arias's avatar
Alam Ed Arias committed
		HU_Drawer();
	}

	// change gamma if needed
	// (GS_LEVEL handles this already due to level-specific palettes)
	if (forcerefresh && gamestate != GS_LEVEL)
Alam Ed Arias's avatar
Alam Ed Arias committed
		V_SetPalette(0);

	wipegamestate = gamestate;
Alam Ed Arias's avatar
Alam Ed Arias committed

	// draw pause pic
	if (paused && cv_showhud.value && !demo.playback)
Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		INT32 py;
		patch_t *patch;
		if (automapactive)
			py = 4;
		else
			py = viewwindowy + 4;
		patch = W_CachePatchName("M_PAUSE", PU_CACHE);
Sryder's avatar
Sryder committed
		V_DrawScaledPatch(viewwindowx + (BASEVIDWIDTH - SHORT(patch->width))/2, py, 0, patch);
Alam Ed Arias's avatar
Alam Ed Arias committed
	}

fickleheart's avatar
fickleheart committed
	if (demo.rewinding)
		V_DrawFadeScreen(TC_RAINBOW, (leveltime & 0x20) ? SKINCOLOR_PASTEL : SKINCOLOR_MOONSLAM);

Alam Ed Arias's avatar
Alam Ed Arias committed
	// vid size change is now finished if it was on...
	vid.recalc = 0;

	// FIXME: draw either console or menu, not the two
	if (gamestate != GS_TIMEATTACK)
		CON_Drawer();

#ifdef HAVE_THREADS
	I_lock_mutex(&m_menu_mutex);
#endif
Alam Ed Arias's avatar
Alam Ed Arias committed
	M_Drawer(); // menu is drawn even on top of everything
#ifdef HAVE_THREADS
	I_unlock_mutex(m_menu_mutex);
#endif
	// focus lost moved to M_Drawer
Alam Ed Arias's avatar
Alam Ed Arias committed

	//
	// wipe update
	//
	if (wipe)
Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		// note: moved up here because NetUpdate does input changes
		// and input during wipe tends to mess things up
		wipedefindex += WIPEFINALSHIFT;

		if (rendermode != render_none)
		{
			F_WipeEndScreen();
			F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK);
		}
Alam Ed Arias's avatar
Alam Ed Arias committed
	NetUpdate(); // send out any new accumulation

	// It's safe to end the game now.
	if (G_GetExitGameFlag())
	{
		Command_ExitGame_f();
		G_ClearExitGameFlag();
	}

	//
	// normal update
	//
	if (!wipe)
	{
		if (cv_netstat.value)
		{
			char s[50];
			Net_GetNetStat();

			s[sizeof s - 1] = '\0';

			snprintf(s, sizeof s - 1, "get %d b/s", getbps);
			V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-40, V_YELLOWMAP, s);
			snprintf(s, sizeof s - 1, "send %d b/s", sendbps);
			V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-30, V_YELLOWMAP, s);
			snprintf(s, sizeof s - 1, "GameMiss %.2f%%", gamelostpercent);
			V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-20, V_YELLOWMAP, s);
			snprintf(s, sizeof s - 1, "SysMiss %.2f%%", lostpercent);
			V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-10, V_YELLOWMAP, s);
		}

		if (cv_shittyscreen.value)
			V_DrawVhsEffect(cv_shittyscreen.value == 2);

		I_FinishUpdate(); // page flip or blit buffer
Alam Ed Arias's avatar
Alam Ed Arias committed
	}
}

// =========================================================================
// D_SRB2Loop
// =========================================================================

tic_t rendergametic;

void D_SRB2Loop(void)
{
Eidolon's avatar
Eidolon committed
	tic_t entertic = 0, oldentertics = 0, realtics = 0, rendertimeout = INFTICS;
	double deltatics = 0.0;
	double deltasecs = 0.0;

	boolean interp = false;
	boolean doDisplay = false;
Alam Ed Arias's avatar
Alam Ed Arias committed

	if (dedicated)
		server = true;

	// Pushing of + parameters is now done back in D_SRB2Main, not here.

#ifdef _WINDOWS
	CONS_Printf("I_StartupMouse()...\n");
	I_DoStartupMouse();
#endif

Eidolon's avatar
Eidolon committed
	I_UpdateTime(cv_timescale.value);
Alam Ed Arias's avatar
Alam Ed Arias committed
	oldentertics = I_GetTime();

	// end of loading screen: CONS_Printf() will no more call FinishUpdate()
	con_startup = false;

	// make sure to do a d_display to init mode _before_ load a level
	SCR_SetMode(); // change video mode
	SCR_Recalc();

	// Check and print which version is executed.
	// Use this as the border between setup and the main game loop being entered.
	CONS_Printf(
	"===========================================================================\n"
	"                   We hope you enjoy this game as\n"
	"                     much as we did making it!\n"
	"===========================================================================\n");

	// hack to start on a nice clear console screen.
	COM_ImmedExecute("cls;version");

	if (rendermode == render_soft)
Sal's avatar
Sal committed
		V_DrawFixedPatch(0, 0, FRACUNIT/2, 0, (patch_t *)W_CacheLumpNum(W_GetNumForName("KARTKREW"), PU_CACHE), NULL);
Alam Ed Arias's avatar
Alam Ed Arias committed
	I_FinishUpdate(); // page flip or blit buffer

	for (;;)
	{
Eidolon's avatar
Eidolon committed
		// capbudget is the minimum precise_t duration of a single loop iteration
		precise_t capbudget;
		precise_t enterprecise = I_GetPreciseTime();
		precise_t finishprecise = enterprecise;

		{
			// Casting the return value of a function is bad practice (apparently)
			double budget = round((1.0 / R_GetFramerateCap()) * I_GetPrecisePrecision());
			capbudget = (precise_t) budget;
		}

		I_UpdateTime(cv_timescale.value);

Alam Ed Arias's avatar
Alam Ed Arias committed
		if (lastwipetic)
		{
			oldentertics = lastwipetic;
			lastwipetic = 0;
		}

		// get real tics
		entertic = I_GetTime();
		realtics = entertic - oldentertics;
		oldentertics = entertic;

Eidolon's avatar
Eidolon committed
		if (demo.playback && gamestate == GS_LEVEL)
		{
			// Nicer place to put this.
			realtics = realtics * cv_playbackspeed.value;
		}
toaster's avatar
toaster committed

Alam Ed Arias's avatar
Alam Ed Arias committed
#ifdef DEBUGFILE
		if (!realtics)
			if (debugload)
				debugload--;
#endif

Eidolon's avatar
Eidolon committed
		interp = R_UsingFrameInterpolation() && !dedicated;
		doDisplay = false;
Alam Ed Arias's avatar
Alam Ed Arias committed

#ifdef HW3SOUND
		HW3S_BeginFrameUpdate();
#endif

Eidolon's avatar
Eidolon committed
		refreshdirmenu = 0; // not sure where to put this, here as good as any?
Alam Ed Arias's avatar
Alam Ed Arias committed

Eidolon's avatar
Eidolon committed
		if (realtics > 0 || singletics)
		{
			// don't skip more than 10 frames at a time
			// (fadein / fadeout cause massive frame skip!)
			if (realtics > 8)
				realtics = 1;

			// process tics (but maybe not if realtic == 0)
			TryRunTics(realtics);

			if (lastdraw || singletics || gametic > rendergametic)
			{
				rendergametic = gametic;
				rendertimeout = entertic + TICRATE/17;

				doDisplay = true;
			}
			else if (rendertimeout < entertic) // in case the server hang or netsplit
			{
				doDisplay = true;
			}
Alam Ed Arias's avatar
Alam Ed Arias committed

Eidolon's avatar
Eidolon committed
			renderisnewtic = true;
		}
		else
Alam Ed Arias's avatar
Alam Ed Arias committed
		{
Eidolon's avatar
Eidolon committed
			renderisnewtic = false;
		}
Alam Ed Arias's avatar
Alam Ed Arias committed

Eidolon's avatar
Eidolon committed
		if (interp)
		{
			renderdeltatics = FLOAT_TO_FIXED(deltatics);
Alam Ed Arias's avatar
Alam Ed Arias committed

			if (!(paused || P_AutoPause()) && deltatics < 1.0 && !hu_stopped)
Eidolon's avatar
Eidolon committed
			{
				rendertimefrac = g_time.timefrac;
			}
			else
			{
				rendertimefrac = FRACUNIT;
			}
Alam Ed Arias's avatar
Alam Ed Arias committed
		}
Eidolon's avatar
Eidolon committed
		else
Alam Ed Arias's avatar
Alam Ed Arias committed
		{
Eidolon's avatar
Eidolon committed
			renderdeltatics = realtics * FRACUNIT;
			rendertimefrac = FRACUNIT;
		}
Alam Ed Arias's avatar
Alam Ed Arias committed

Eidolon's avatar
Eidolon committed
		if (interp || doDisplay)
		{
			D_Display();
Alam Ed Arias's avatar
Alam Ed Arias committed
		}

		// Only take screenshots after drawing.
		if (moviemode)
			M_SaveFrame();
		if (takescreenshot)
			M_DoScreenShot();

Sal's avatar
Sal committed
		// consoleplayer -> displayplayers (hear sounds from viewpoint)
Alam Ed Arias's avatar
Alam Ed Arias committed
		S_UpdateSounds(); // move positional sounds

		// check for media change, loop music..
		I_UpdateCD();

#ifdef HW3SOUND
		HW3S_EndFrameUpdate();
#endif

#ifdef HAVE_BLUA
		LUA_Step();
#endif

#ifdef HAVE_DISCORDRPC
James R.'s avatar
James R. committed
		if (! dedicated)
		{
			Discord_RunCallbacks();
		}
#endif
Eidolon's avatar
Eidolon committed

		// Fully completed frame made.
		finishprecise = I_GetPreciseTime();
		if (!singletics)
		{
			INT64 elapsed = (INT64)(finishprecise - enterprecise);

			// in the case of "match refresh rate" + vsync, don't sleep at all
			const boolean vsync_with_match_refresh = cv_vidwait.value && cv_fpscap.value == 0;

			if (elapsed > 0 && (INT64)capbudget > elapsed && !vsync_with_match_refresh)
Eidolon's avatar
Eidolon committed
			{
				I_SleepDuration(capbudget - (finishprecise - enterprecise));
			}
		}
		// Capture the time once more to get the real delta time.
		finishprecise = I_GetPreciseTime();
		deltasecs = (double)((INT64)(finishprecise - enterprecise)) / I_GetPrecisePrecision();
		deltatics = deltasecs * NEWTICRATE;
Alam Ed Arias's avatar
Alam Ed Arias committed
	}
}

// =========================================================================
// D_SRB2Main
// =========================================================================

//
// D_StartTitle
//
void D_StartTitle(void)
{
Alam Ed Arias's avatar
Alam Ed Arias committed
	INT32 i;
Alam Ed Arias's avatar
Alam Ed Arias committed
	if (netgame)
	{
		if (gametype == GT_RACE) // SRB2kart
Alam Ed Arias's avatar
Alam Ed Arias committed
		{
			G_SetGamestate(GS_WAITINGPLAYERS); // hack to prevent a command repeat

			if (server)
			{
				char mapname[6];

				strlcpy(mapname, G_BuildMapName(spstage_start), sizeof (mapname));
				strlwr(mapname);
				mapname[5] = '\0';

				COM_BufAddText(va("map %s\n", mapname));
			}
		}

		return;
	}

	// okay, stop now
	// (otherwise the game still thinks we're playing!)
	SV_StopServer();
Alam Ed Arias's avatar
Alam Ed Arias committed
	SV_ResetServer();
Alam Ed Arias's avatar
Alam Ed Arias committed

Alam Ed Arias's avatar
Alam Ed Arias committed
	for (i = 0; i < MAXPLAYERS; i++)
		CL_ClearPlayer(i);

	splitscreen = 0;
Alam Ed Arias's avatar
Alam Ed Arias committed
	SplitScreen_OnChange();
	botingame = false;
	botskin = 0;
	cv_debug = 0;
	emeralds = 0;

Alam Ed Arias's avatar
Alam Ed Arias committed
	// In case someone exits out at the same time they start a time attack run,
	// reset modeattacking
	modeattacking = ATTACKING_NONE;

	// empty maptol so mario/etc sounds don't play in sound test when they shouldn't
	maptol = 0;

	gameaction = ga_nothing;
Sal's avatar
Sal committed
	memset(displayplayers, 0, sizeof(displayplayers));
	consoleplayer = 0;
Alam Ed Arias's avatar
Alam Ed Arias committed
	//demosequence = -1;
	gametype = GT_RACE; // SRB2kart
Alam Ed Arias's avatar
Alam Ed Arias committed
	paused = false;
	F_StartTitleScreen();

	// Reset the palette -- SRB2Kart: actually never mind let's do this in the middle of every fade
	/*if (rendermode != render_none)
		V_SetPaletteLump("PLAYPAL");*/
Alam Ed Arias's avatar
Alam Ed Arias committed
}

//
// D_AddFile
//
static void D_AddFile(const char *file, char **filearray)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	size_t pnumwadfiles;
	char *newfile;

	for (pnumwadfiles = 0; filearray[pnumwadfiles]; pnumwadfiles++)
Alam Ed Arias's avatar
Alam Ed Arias committed
		;

	newfile = malloc(strlen(file) + 1);
	if (!newfile)
	{
		I_Error("No more free memory to AddFile %s",file);
	}
	strcpy(newfile, file);

	filearray[pnumwadfiles] = newfile;
Alam Ed Arias's avatar
Alam Ed Arias committed
}

static inline void D_CleanFile(char **filearray)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	size_t pnumwadfiles;
	for (pnumwadfiles = 0; filearray[pnumwadfiles]; pnumwadfiles++)
Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		free(filearray[pnumwadfiles]);
		filearray[pnumwadfiles] = NULL;
Alam Ed Arias's avatar
Alam Ed Arias committed
	}
}

// ==========================================================================
// Identify the SRB2 version, and IWAD file to use.
// ==========================================================================

James R.'s avatar
James R. committed
static boolean AddIWAD(void)
James R.'s avatar
James R. committed
	char * path = va(pandf,srb2path,"srb2.srb");

	if (FIL_ReadFileOK(path))
James R.'s avatar
James R. committed
		D_AddFile(path, startupwadfiles);
Alam Ed Arias's avatar
Alam Ed Arias committed
static void IdentifyVersion(void)
{
	const char *srb2waddir = NULL;

Alam Ed Arias's avatar
Alam Ed Arias committed
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
Alam Ed Arias's avatar
Alam Ed Arias committed
	// change to the directory where 'srb2.srb' is found
	srb2waddir = I_LocateWad();
#endif

	// get the current directory (possible problem on NT with "." as current dir)
	if (srb2waddir)
	{
		strlcpy(srb2path,srb2waddir,sizeof (srb2path));
	}
	else
	{
#if !defined(_WIN32_WCE) && !defined(_PS3)
		if (getcwd(srb2path, 256) != NULL)
			srb2waddir = srb2path;
		else
#endif
		{
#ifdef _arch_dreamcast
			srb2waddir = "/cd";
#else
James R.'s avatar
James R. committed
			srb2waddir = srb2path;
Alam Ed Arias's avatar
Alam Ed Arias committed
#endif
		}
	}

	// Load the IWAD
James R.'s avatar
James R. committed
	if (! AddIWAD())
	{
		I_Error("SRB2.SRB not found! Expected in %s\n", srb2waddir);
	}
Alam Ed Arias's avatar
Alam Ed Arias committed

James R.'s avatar
James R. committed
	// will be overwritten in case of -cdrom or unix/win home
	snprintf(configfile, sizeof configfile, "%s" PATHSEP CONFIGFILENAME, srb2waddir);
	configfile[sizeof configfile - 1] = '\0';
Alam Ed Arias's avatar
Alam Ed Arias committed

	// if you change the ordering of this or add/remove a file, be sure to update the md5
	// checking in D_SRB2Main

#ifdef USE_PATCH_DTA
Alam Ed Arias's avatar
Alam Ed Arias committed
	// Add our crappy patches to fix our bugs
	D_AddFile(va(pandf,srb2waddir,"patch.dta"));
Alam Ed Arias's avatar
Alam Ed Arias committed

	D_AddFile(va(pandf,srb2waddir,"gfx.kart"), startupwadfiles);
	D_AddFile(va(pandf,srb2waddir,"textures.kart"), startupwadfiles);
	D_AddFile(va(pandf,srb2waddir,"chars.kart"), startupwadfiles);
	D_AddFile(va(pandf,srb2waddir,"maps.kart"), startupwadfiles);
SeventhSentinel's avatar
SeventhSentinel committed
#ifdef USE_PATCH_KART
	D_AddFile(va(pandf,srb2waddir,"patch.kart"), startupwadfiles);
SeventhSentinel's avatar
SeventhSentinel committed
#endif

Alam Ed Arias's avatar
Alam Ed Arias committed
#if !defined (HAVE_SDL) || defined (HAVE_MIXER)
#define MUSICTEST(str) \
	{\
		const char *musicpath = va(pandf,srb2waddir,str);\
		int ms = W_VerifyNMUSlumps(musicpath); \
		if (ms == 1) \
			D_AddFile(musicpath, startupwadfiles); \
		else if (ms == 0) \
			I_Error("File "str" has been modified with non-music/sound lumps"); \
	}
	MUSICTEST("sounds.kart")
	MUSICTEST("music.kart")