shithub: heretic

ref: 3191e80bf7fda36bf0c951b0363cd52fa6ceac6b
dir: /d_main.c/

View raw version
// D_main.c

// HEADER FILES ------------------------------------------------------------

#include "h2stdinc.h"
#if defined(__WATCOMC__) && defined(_DOS)
#include <dos.h>
#include <graph.h>
#include <direct.h>
#endif
#include "doomdef.h"
#include "p_local.h"
#include "soundst.h"
#include "v_compat.h"
#include "i_system.h"

// MACROS ------------------------------------------------------------------

#define MAXWADFILES		20
#define SHAREWAREWADNAME	"heretic1.wad"

#ifdef RENDER3D
#define V_DrawPatch(x,y,p)		OGL_DrawPatch((x),(y),(p))
#define V_DrawRawScreen(a)		OGL_DrawRawScreen((a))
#endif

// TYPES -------------------------------------------------------------------

// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------

void D_CheckNetGame(void);
void G_BuildTiccmd(ticcmd_t *cmd);
void F_Drawer(void);
boolean F_Responder(event_t *ev);
void R_ExecuteSetViewSize(void);

// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------

void D_ProcessEvents(void);
void D_DoAdvanceDemo(void);
void D_AdvanceDemo (void);
void D_PageDrawer (void);

// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------

// EXTERNAL DATA DECLARATIONS ----------------------------------------------

extern boolean MenuActive;
extern boolean askforquit;

// PUBLIC DATA DEFINITIONS -------------------------------------------------

char *basePath = DUMMY_BASEPATH;
boolean shareware = false;		// true if only episode 1 present
boolean ExtendedWAD = false;	// true if episodes 4 and 5 present

boolean nomonsters;			// checkparm of -nomonsters
boolean respawnparm;			// checkparm of -respawn
boolean debugmode;			// checkparm of -debug
boolean ravpic;				// checkparm of -ravpic
boolean singletics;			// debug flag to cancel adaptiveness
boolean noartiskip;			// whether shift-enter skips an artifact

skill_t startskill;
int startepisode;
int startmap;
boolean autostart;
boolean advancedemo;
FILE *debugfile;
event_t events[MAXEVENTS];
int eventhead;
int eventtail;

// PRIVATE DATA DEFINITIONS ------------------------------------------------

static int demosequence;
static int pagetic;
static const char *pagename;
static char basedefault[1024];

static const char *wadfiles[MAXWADFILES + 1] =
{
	"heretic.wad",
	NULL	/* the last entry MUST be NULL */
};

// CODE --------------------------------------------------------------------

#if !(defined(__WATCOMC__) || defined(__DJGPP__) || defined(_WIN32))
char *strlwr (char *str)
{
	char	*c;
	c = str;
	while (*c)
	{
		*c = tolower(*c);
		c++;
	}
	return str;
}

char *strupr (char *str)
{
	char	*c;
	c = str;
	while (*c)
	{
		*c = toupper(*c);
		c++;
	}
	return str;
}

int filelength(int handle)
{
	Dir *d;
	int length;

	d = dirfstat(handle);
	if (d == nil)
	{
		I_Error("Error fstating");
	}
	length = d->length;
	free(d);
	return length;
}
#endif

//==========================================================================
//
// Fixed Point math
//
//==========================================================================

#if defined(_HAVE_FIXED_ASM)

#if defined(__i386__) || defined(__386__) || defined(_M_IX86)
#if defined(__GNUC__) && !defined(_INLINE_FIXED_ASM)
fixed_t	FixedMul (fixed_t a, fixed_t b)
{
	fixed_t retval;
	__asm__ __volatile__(
		"imull  %%edx			\n\t"
		"shrdl  $16, %%edx, %%eax	\n\t"
		: "=a" (retval)
		: "a" (a), "d" (b)
		: "cc"
	);
	return retval;
}

fixed_t	FixedDiv2 (fixed_t a, fixed_t b)
{
	fixed_t retval;
	__asm__ __volatile__(
		"cdq				\n\t"
		"shldl  $16, %%eax, %%edx	\n\t"
		"sall   $16, %%eax		\n\t"
		"idivl  %%ebx			\n\t"
		: "=a" (retval)
		: "a" (a), "b" (b)
		: "%edx", "cc"
	);
	return retval;
}
#endif	/* GCC and !_INLINE_FIXED_ASM */
#endif	/* x86 */

#else	/* C-only versions */

fixed_t FixedMul (fixed_t a, fixed_t b)
{
	return ((int64_t) a * (int64_t) b) >> 16;
}

fixed_t FixedDiv2 (fixed_t a, fixed_t b)
{
	if (!b)
		return 0;
	return (fixed_t) (((double) a / (double) b) * FRACUNIT);
}
#endif

fixed_t FixedDiv (fixed_t a, fixed_t b)
{
	if ((abs(a) >> 14) >= abs(b))
	{
		return ((a^b) < 0 ? H2MININT : H2MAXINT);
	}
	return (FixedDiv2(a, b));
}

//==========================================================================
//
// Byte swap functions
//
//==========================================================================

int16_t ShortSwap (int16_t x)
{
	return (int16_t) (((uint16_t)x << 8) | ((uint16_t)x >> 8));
}

int32_t LongSwap (int32_t x)
{
	return (int32_t) (((uint32_t)x << 24) | ((uint32_t)x >> 24) |
			  (((uint32_t)x & (uint32_t)0x0000ff00UL) << 8) |
			  (((uint32_t)x & (uint32_t)0x00ff0000UL) >> 8));
}

/*
===============================================================================

							EVENT HANDLING

Events are asyncronous inputs generally generated by the game user.

Events can be discarded if no responder claims them

===============================================================================
*/

//---------------------------------------------------------------------------
//
// PROC D_PostEvent
//
// Called by the I/O functions when input is detected.
//
//---------------------------------------------------------------------------

QLock		eventlock;


//
// D_PostEvent
// Called by the I/O functions when input is detected
//
void D_PostEvent (event_t* ev)
{
    int next;

retry:
    qlock(&eventlock);
    next = (eventhead+1)&(MAXEVENTS-1);
    if(next == eventtail){
        qunlock(&eventlock);
        if(ev->type != ev_keydown && ev->type != ev_keyup)
            return;
        sleep(1);
        goto retry;
    }
    events[eventhead] = *ev;
    eventhead = next;
    qunlock(&eventlock);
}

//---------------------------------------------------------------------------
//
// PROC D_ProcessEvents
//
// Send all the events of the given timestamp down the responder chain.
//
//---------------------------------------------------------------------------

void D_ProcessEvents(void)
{
	event_t *ev;

	while (eventtail != eventhead)
	{
		ev = &events[eventtail];
		if (F_Responder(ev))
		{
			goto _next_ev;
		}
		if (MN_Responder(ev))
		{
			goto _next_ev;
		}
		G_Responder(ev);
	_next_ev:
		eventtail = (eventtail + 1) & (MAXEVENTS - 1);
	}
}

//---------------------------------------------------------------------------
//
// PROC DrawMessage
//
//---------------------------------------------------------------------------

void DrawMessage(void)
{
	player_t *player;

	player = &players[consoleplayer];
	if (player->messageTics <= 0 || !player->message)
	{ // No message
		return;
	}
	MN_DrTextA(player->message, 160 - MN_TextAWidth(player->message)/2, 1);
}

//---------------------------------------------------------------------------
//
// PROC D_Display
//
// Draw current display, possibly wiping it from the previous.
//
//---------------------------------------------------------------------------

void D_Display(void)
{
	// Change the view size if needed
	if (setsizeneeded)
	{
		R_ExecuteSetViewSize();
	}

//
// do buffered drawing
//
	switch (gamestate)
	{
	case GS_LEVEL:
		if (!gametic)
			break;
#if  AM_TRANSPARENT
		R_RenderPlayerView (&players[displayplayer]);
#endif
		if (automapactive)
			AM_Drawer ();
#if !AM_TRANSPARENT
		else
		{
			R_RenderPlayerView (&players[displayplayer]);
		}
#endif
		CT_Drawer();
		UpdateState |= I_FULLVIEW;
		SB_Drawer();
		break;
	case GS_INTERMISSION:
		IN_Drawer ();
		break;
	case GS_FINALE:
		F_Drawer ();
		break;
	case GS_DEMOSCREEN:
		D_PageDrawer ();
		break;
	}

	if (paused && !MenuActive && !askforquit)
	{
		if (!netgame)
		{
			V_DrawPatch(160, viewwindowy + 5, (PATCH_REF)WR_CacheLumpName("PAUSED", PU_CACHE));
		}
		else
		{
			V_DrawPatch(160, 70, (PATCH_REF)WR_CacheLumpName("PAUSED", PU_CACHE));
		}
	}

#ifdef RENDER3D
	if (OGL_DrawFilter())
		BorderNeedRefresh = true;
#endif

	// Handle player messages
	DrawMessage();

	// Menu drawing
	MN_Drawer();

	// Send out any new accumulation
	NetUpdate();

	// Flush buffered stuff to screen
	I_Update();
}

//---------------------------------------------------------------------------
//
// PROC D_DoomLoop
//
//---------------------------------------------------------------------------

void D_DoomLoop(void)
{
	if (M_CheckParm("-debugfile"))
	{
		char filename[20];
		snprintf(filename, sizeof(filename), "debug%i.txt", consoleplayer);
		debugfile = fopen(filename,"w");
	}
	I_InitGraphics();
	while (1)
	{
		// Frame syncronous IO operations
		I_StartFrame();

		// Process one or more tics
		if (singletics)
		{
			I_StartTic();
			D_ProcessEvents();
			G_BuildTiccmd(&netcmds[consoleplayer][maketic%BACKUPTICS]);
			if (advancedemo)
				D_DoAdvanceDemo ();
			G_Ticker();
			gametic++;
			maketic++;
		}
		else
		{
			// Will run at least one tic
			TryRunTics();
		}

		// Move positional sounds
		S_UpdateSounds(players[consoleplayer].mo);
		D_Display();
	}
}

/*
===============================================================================

						DEMO LOOP

===============================================================================
*/

/*
================
=
= D_PageTicker
=
= Handles timing for warped projection
=
================
*/

void D_PageTicker (void)
{
	if (--pagetic < 0)
		D_AdvanceDemo ();
}


/*
================
=
= D_PageDrawer
=
================
*/

extern boolean MenuActive;

void D_PageDrawer(void)
{
	V_DrawRawScreen((BYTE_REF)WR_CacheLumpName(pagename, PU_CACHE));
	if (demosequence == 1)
	{
		V_DrawPatch(4, 160, (PATCH_REF)WR_CacheLumpName("ADVISOR", PU_CACHE));
	}
	UpdateState |= I_FULLSCRN;
}

/*
=================
=
= D_AdvanceDemo
=
= Called after each demo or intro demosequence finishes
=================
*/

void D_AdvanceDemo (void)
{
	advancedemo = true;
}

void D_DoAdvanceDemo (void)
{
	players[consoleplayer].playerstate = PST_LIVE; // don't reborn
	advancedemo = false;
	usergame = false; // can't save / end game here
	paused = false;
	gameaction = ga_nothing;
	demosequence = (demosequence + 1) % 7;
	switch (demosequence)
	{
	case 0:
		pagetic = 210;
		gamestate = GS_DEMOSCREEN;
		pagename = "TITLE";
		S_StartSong(mus_titl, false);
		break;
	case 1:
		pagetic = 140;
		gamestate = GS_DEMOSCREEN;
		pagename = "TITLE";
		break;
	case 2:
		BorderNeedRefresh = true;
		UpdateState |= I_FULLSCRN;
		G_DeferedPlayDemo ("demo1");
		break;
	case 3:
		pagetic = 200;
		gamestate = GS_DEMOSCREEN;
		pagename = "CREDIT";
		break;
	case 4:
		BorderNeedRefresh = true;
		UpdateState |= I_FULLSCRN;
		G_DeferedPlayDemo ("demo2");
		break;
	case 5:
		pagetic = 200;
		gamestate = GS_DEMOSCREEN;
		if (shareware)
		{
			pagename = "ORDER";
		}
		else
		{
			pagename = "CREDIT";
		}
		break;
	case 6:
		BorderNeedRefresh = true;
		UpdateState |= I_FULLSCRN;
		G_DeferedPlayDemo ("demo3");
		break;
	}
}


/*
=================
=
= D_StartTitle
=
=================
*/

void D_StartTitle (void)
{
	gameaction = ga_nothing;
	demosequence = -1;
	D_AdvanceDemo ();
}


/*
==============
=
= D_CheckRecordFrom
=
= -recordfrom <savegame num> <demoname>
==============
*/

void D_CheckRecordFrom (void)
{
	int p;

	p = M_CheckParm ("-recordfrom");
	if (!p || p >= myargc - 2)
		return;
	G_LoadGame(atoi(myargv[p + 1]));
	G_DoLoadGame(); // load the gameskill etc info from savegame
	G_RecordDemo(gameskill, 1, gameepisode, gamemap, myargv[p + 2]);
	D_DoomLoop(); // never returns
}

/*
===============
=
= D_AddFile
=
===============
*/

void D_AddFile(const char *file)
{
	int i = 0;
	char *newwad;

	while (wadfiles[i])
	{
		if (++i == MAXWADFILES)
			I_Error ("MAXWADFILES reached for %s", file);
	}
	newwad = (char *) malloc(strlen(file) + 1);
	strcpy(newwad, file);
	wadfiles[i] = newwad;
	if (++i <= MAXWADFILES)
		wadfiles[i] = NULL;
}

//==========================================================
//
//  Startup Thermo code
//  FIXME : MOVE OR REMOVE THIS...
//==========================================================
#if defined(__WATCOMC__) && defined(_DOS)

#define MSG_Y		9
/*
#define THERM_X		15
#define THERM_Y		16
#define THERMCOLOR	3
*/
#define THERM_X		14
#define THERM_Y		14

static int thermMax;
static int thermCurrent;

//
//  Heretic startup screen shit
//
static byte *hscreen;
static char *startup;	// * to text screen
static char smsg[80];	// status bar line
static char tmsg[300];

static void hgotoxy(int x,int y)
{
	hscreen = (byte *)(0xb8000 + y*160 + x*2);
}

static void hput(unsigned char c, unsigned char a)
{
	*hscreen++ = c;
	*hscreen++ = a;
}

static void hprintf(const char *string, unsigned char a)
{
	int i;

	if (debugmode)
	{
		puts(string);
		return;
	}
	for (i = 0; i < strlen(string); i++)
	{
		hput(string[i], a);
	}
}

static void drawstatus(void)
{
	if(debugmode)
	{
		return;
	}
	_settextposition(25, 2);
	_setbkcolor(1);
	_settextcolor(15);
	_outtext(smsg);
	_settextposition(25, 1);
}

static void status(const char *string)
{
	strcat(smsg, string);
	drawstatus();
}

static void DrawThermo(void)
{
	unsigned char *screen;
	int progress;
	int i;

	if (debugmode)
	{
		return;
	}
#if 0
	progress = (98*thermCurrent)/thermMax;
	screen = (char *)0xb8000 + (THERM_Y*160 + THERM_X*2);
	for (i = 0; i < progress/2; i++)
	{
		switch(i)
		{
		case 4:
		case 9:
		case 14:
		case 19:
		case 29:
		case 34:
		case 39:
		case 44:
			*screen++ = 0xb3;
			*screen++ = (THERMCOLOR<<4)+15;
			break;
		case 24:
			*screen++ = 0xba;
			*screen++ = (THERMCOLOR<<4)+15;
			break;
		default:
			*screen++ = 0xdb;
			*screen++ = 0x40 + THERMCOLOR;
			break;
		}
	}
	if (progress & 1)
	{
		*screen++ = 0xdd;
		*screen++ = 0x40 + THERMCOLOR;
	}
#else
	progress = (50*thermCurrent)/thermMax + 2;
//	screen = (char *)0xb8000 + (THERM_Y*160 + THERM_X*2);
	hgotoxy(THERM_X,THERM_Y);
	for (i = 0; i < progress; i++)
	{
//		*screen++ = 0xdb;
//		*screen++ = 0x2a;
		hput(0xdb, 0x2a);
	}
#endif
}

static void blitStartup(void)
{
	byte *textScreen;

	if(debugmode)
	{
		return;
	}

	// Blit main screen
	textScreen = (byte *)0xb8000;
	memcpy(textScreen, startup, 4000);

	// Print version string
	_setbkcolor(4); // Red
	_settextcolor(14); // Yellow
	_settextposition(3, 47);
	_outtext(VERSION_TEXT);

	// Hide cursor
	_settextcursor(0x2000);
}

void tprintf(const char *msg, int initflag)
{
# if 0
	char temp[80];
	int i, start, add;

	if (debugmode)
	{
		printf(msg);
		return;
	}
	if (initflag)
		tmsg[0] = 0;
	strcat(tmsg,msg);
	blitStartup();
	DrawThermo();
	_setbkcolor(4);
	_settextcolor(15);
	for (add = start = i = 0; i <= strlen(tmsg); i++)
	{
		if ((tmsg[i] == '\n') || (!tmsg[i]))
		{
			memset(temp,0,80);
			strncpy(temp,tmsg+start,i-start);
			_settextposition(MSG_Y+add,40-strlen(temp)/2);
			_outtext(temp);
			start = i+1;
			add++;
		}
	}
	_settextposition(25,1);
	drawstatus();
# endif
}

void CheckAbortStartup(void)
{
#if defined(__WATCOMC__) && defined(_DOS)
	extern int lastpress;
	if (lastpress == 1)
	{ // Abort if escape pressed
		union REGS regs;
		I_ShutdownKeyboard();
		regs.x.eax = 0x3;
		int386(0x10, &regs, &regs);
		printf("Exited from HERETIC.\n");
		exit(1);
	}
#endif
}

void IncThermo(void)
{
	thermCurrent++;
	DrawThermo();
	CheckAbortStartup();
}

void InitThermo(int max)
{
	thermMax = max;
	thermCurrent = 0;
}

#else

#define hgotoxy(x,y)	do {} while (0)
#define hprintf(s,a)	puts((s))
#define status(s)	puts((s))
#define DrawThermo()	do {} while (0)
void tprintf(const char *msg, int initflag)
{
# if 0
	printf(msg);
# endif
}
void CheckAbortStartup(void) {}
void IncThermo(void) {}
void InitThermo(int x) {}
#endif

//---------------------------------------------------------------------------
//
// PROC D_DoomMain
//
//---------------------------------------------------------------------------

void D_DoomMain(void)
{
	int p, e, m, i;
	char file[MAX_OSPATH];
	boolean devMap;
	char *slash;
	char *wadloc[nelem(wadfiles)];

	M_FindResponseFile();
	setbuf(stdout, NULL);
	nomonsters = M_CheckParm("-nomonsters");
	respawnparm = M_CheckParm("-respawn");
	ravpic = M_CheckParm("-ravpic");
	noartiskip = M_CheckParm("-noartiskip");
	debugmode = M_CheckParm("-debug");
	startskill = sk_medium;
	startepisode = 1;
	startmap = 1;
	autostart = false;

	// -FILE [filename] [filename] ...
	// Add files to the wad list.
	p = M_CheckParm("-file");
	if (p)
	{	// the parms after p are wadfile/lump names, until end of parms
		// or another - preceded parm
		while (++p != myargc && myargv[p][0] != '-')
		{
			D_AddFile(myargv[p]);
		}
	}

	// -DEVMAP <episode> <map>
	// Adds a map wad from the development directory to the wad list,
	// and sets the start episode and the start map.
	devMap = false;
	p = M_CheckParm("-devmap");
	if (p && p < myargc-2)
	{
		e = myargv[p+1][0];
		m = myargv[p+2][0];
		snprintf(file, sizeof(file), "%se%cm%c.wad", DEVMAPDIR, e, m);
		D_AddFile(file);
		printf("DEVMAP: Episode %c, Map %c.\n", e, m);
		startepisode = e-'0';
		startmap = m-'0';
		autostart = true;
		devMap = true;
	}

	p = M_CheckParm("-playdemo");
	if (!p)
	{
		p = M_CheckParm("-timedemo");
	}
	if (p && p < myargc-1)
	{
		snprintf(file, sizeof(file), "%s.lmp", myargv[p+1]);
		D_AddFile(file);
		printf("Playing demo %s.lmp.\n", myargv[p+1]);
	}

//
// get skill / episode / map from parms
//
	if (M_CheckParm("-deathmatch"))
	{
		deathmatch = true;
	}

	p = M_CheckParm("-skill");
	if (p && p < myargc-1)
	{
		startskill = myargv[p+1][0]-'1';
		autostart = true;
	}

	p = M_CheckParm("-episode");
	if (p && p < myargc-1)
	{
		startepisode = myargv[p+1][0]-'0';
		startmap = 1;
		autostart = true;
	}

	p = M_CheckParm("-warp");
	if (p && p < myargc-2)
	{
		startepisode = myargv[p+1][0]-'0';
		startmap = myargv[p+2][0]-'0';
		autostart = true;
	}

//
// init subsystems
//
	printf("V_Init: allocate screens.\n");
	V_Init();

	printf("Z_Init: Init zone memory allocation daemon.\n");
	Z_Init();

	printf("W_Init: Init WADfiles.\n");
	I_SetupPath(wadfiles);
	W_InitMultipleFiles(wadfiles);

	if (W_CheckNumForName("E2M1") == -1)
	{ // Can't find episode 2 maps, must be the shareware WAD
		shareware = true;
	}
	else if (W_CheckNumForName("EXTENDED") != -1)
	{ // Found extended lump, must be the extended WAD
		ExtendedWAD = true;
	}

	// Load defaults before initing other systems
	printf("M_LoadDefaults: Load system defaults.\n");
	M_LoadDefaults("cfg");

#if defined(__WATCOMC__) && defined(_DOS)
	I_StartupKeyboard();
	I_StartupJoystick();
	startup = (char *) W_CacheLumpName("LOADING", PU_CACHE);
	blitStartup();

	//  Build status bar line!
	smsg[0] = 0;
#endif
	if (deathmatch)
		status("DeathMatch...");
	if (nomonsters)
		status("No Monsters...");
	if (respawnparm)
		status("Respawning...");
	if (autostart)
	{
		char temp[64];
		snprintf(temp, sizeof(temp), "Warp to Episode %d, Map %d, Skill %d ",
					startepisode, startmap, startskill + 1);
		status(temp);
	}

	tprintf("MN_Init: Init menu system.\n",1);
	MN_Init();

	CT_Init();

	tprintf("R_Init: Init Heretic refresh daemon.",1);
	hgotoxy(17,7);
	hprintf("Loading graphics",0x3f);
	R_Init();

	tprintf("P_Init: Init Playloop state.",1);
	hgotoxy(17,8);
	hprintf("Init game engine.",0x3f);
	P_Init();
	IncThermo();

	tprintf("I_Init: Setting up machine state.\n",1);
	I_Init();
	IncThermo();

	tprintf("D_CheckNetGame: Checking network game status.\n",1);
	hgotoxy(17,9);
	hprintf("Checking network game status.", 0x3f);
	D_CheckNetGame();
	IncThermo();

#if defined(__WATCOMC__) && defined(_DOS)
	I_CheckExternDriver(); // Check for an external device driver
#endif

	tprintf("SB_Init: Loading patches.\n",1);
	SB_Init();
	IncThermo();

//
// start the apropriate game based on parms
//

	D_CheckRecordFrom();

	p = M_CheckParm("-record");
	if (p && p < myargc-1)
	{
		G_RecordDemo(startskill, 1, startepisode, startmap, myargv[p+1]);
		D_DoomLoop(); // Never returns
	}

	p = M_CheckParm("-playdemo");
	if (p && p < myargc-1)
	{
		singledemo = true; // Quit after one demo
		G_DeferedPlayDemo(myargv[p+1]);
		D_DoomLoop(); // Never returns
	}

	p = M_CheckParm("-timedemo");
	if (p && p < myargc-1)
	{
		G_TimeDemo(myargv[p+1]);
		D_DoomLoop(); // Never returns
	}

	p = M_CheckParm("-loadgame");
	if (p && p < myargc-1)
	{
		G_LoadGame(atoi(myargv[p+1]));
	}

	// Check valid episode and map
	if ((autostart || netgame) && (devMap == false))
	{
		if (M_ValidEpisodeMap(startepisode, startmap) == false)
		{
			startepisode = 1;
			startmap = 1;
		}
	}

	if (gameaction != ga_loadgame)
	{
		UpdateState |= I_FULLSCRN;
		BorderNeedRefresh = true;
		if (autostart || netgame)
		{
			G_InitNew(startskill, startepisode, startmap);
		}
		else
		{
			D_StartTitle();
		}
	}
#if defined(__WATCOMC__) && defined(_DOS)
	_settextcursor(0x0607); // bring the cursor back
#endif
	D_DoomLoop(); // Never returns
}