From 66a2cc66d7504c9b64e1c461e62ad2a9d964fa95 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 23 Jul 2005 16:19:41 +0000 Subject: Initial revision Subversion-branch: /trunk/chocolate-doom Subversion-revision: 4 --- src/d_main.c | 1174 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1174 insertions(+) create mode 100644 src/d_main.c (limited to 'src/d_main.c') diff --git a/src/d_main.c b/src/d_main.c new file mode 100644 index 00000000..41681ff1 --- /dev/null +++ b/src/d_main.c @@ -0,0 +1,1174 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id: d_main.c 4 2005-07-23 16:19:41Z fraggle $ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log$ +// Revision 1.1 2005/07/23 16:20:34 fraggle +// Initial revision +// +// +// DESCRIPTION: +// DOOM main program (D_DoomMain) and game loop (D_DoomLoop), +// plus functions to determine game mode (shareware, registered), +// parse command line parameters, configure game parameters (turbo), +// and call the startup functions. +// +//----------------------------------------------------------------------------- + + +static const char rcsid[] = "$Id: d_main.c 4 2005-07-23 16:19:41Z fraggle $"; + +#define BGCOLOR 7 +#define FGCOLOR 8 + + +#ifdef NORMALUNIX +#include +#include +#include +#include +#include +#include +#endif + + +#include "doomdef.h" +#include "doomstat.h" + +#include "dstrings.h" +#include "sounds.h" + + +#include "z_zone.h" +#include "w_wad.h" +#include "s_sound.h" +#include "v_video.h" + +#include "f_finale.h" +#include "f_wipe.h" + +#include "m_argv.h" +#include "m_misc.h" +#include "m_menu.h" + +#include "i_system.h" +#include "i_sound.h" +#include "i_video.h" + +#include "g_game.h" + +#include "hu_stuff.h" +#include "wi_stuff.h" +#include "st_stuff.h" +#include "am_map.h" + +#include "p_setup.h" +#include "r_local.h" + + +#include "d_main.h" + +// +// D-DoomLoop() +// Not a globally visible function, +// just included for source reference, +// called by D_DoomMain, never exits. +// Manages timing and IO, +// calls all ?_Responder, ?_Ticker, and ?_Drawer, +// calls I_GetTime, I_StartFrame, and I_StartTic +// +void D_DoomLoop (void); + + +char* wadfiles[MAXWADFILES]; + + +boolean devparm; // started game with -devparm +boolean nomonsters; // checkparm of -nomonsters +boolean respawnparm; // checkparm of -respawn +boolean fastparm; // checkparm of -fast + +boolean drone; + +boolean singletics = false; // debug flag to cancel adaptiveness + + + +//extern int soundVolume; +//extern int sfxVolume; +//extern int musicVolume; + +extern boolean inhelpscreens; + +skill_t startskill; +int startepisode; +int startmap; +boolean autostart; + +FILE* debugfile; + +boolean advancedemo; + + + + +char wadfile[1024]; // primary wad file +char mapdir[1024]; // directory of development maps +char basedefault[1024]; // default file + + +void D_CheckNetGame (void); +void D_ProcessEvents (void); +void G_BuildTiccmd (ticcmd_t* cmd); +void D_DoAdvanceDemo (void); + + +// +// EVENT HANDLING +// +// Events are asynchronous inputs generally generated by the game user. +// Events can be discarded if no responder claims them +// +event_t events[MAXEVENTS]; +int eventhead; +int eventtail; + + +// +// D_PostEvent +// Called by the I/O functions when input is detected +// +void D_PostEvent (event_t* ev) +{ + events[eventhead] = *ev; + eventhead = (++eventhead)&(MAXEVENTS-1); +} + + +// +// D_ProcessEvents +// Send all the events of the given timestamp down the responder chain +// +void D_ProcessEvents (void) +{ + event_t* ev; + + // IF STORE DEMO, DO NOT ACCEPT INPUT + if ( ( gamemode == commercial ) + && (W_CheckNumForName("map01")<0) ) + return; + + for ( ; eventtail != eventhead ; eventtail = (++eventtail)&(MAXEVENTS-1) ) + { + ev = &events[eventtail]; + if (M_Responder (ev)) + continue; // menu 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 +gamestate_t wipegamestate = GS_DEMOSCREEN; +extern boolean setsizeneeded; +extern int showMessages; +void R_ExecuteSetViewSize (void); + +void D_Display (void) +{ + static boolean viewactivestate = false; + static boolean menuactivestate = false; + static boolean inhelpscreensstate = false; + static boolean fullscreen = false; + static gamestate_t oldgamestate = -1; + static int borderdrawcount; + int nowtime; + int tics; + int wipestart; + int y; + boolean done; + boolean wipe; + boolean redrawsbar; + + if (nodrawers) + return; // for comparative timing / profiling + + redrawsbar = false; + + // change the view size if needed + if (setsizeneeded) + { + R_ExecuteSetViewSize (); + oldgamestate = -1; // force background redraw + borderdrawcount = 3; + } + + // save the current screen if about to wipe + if (gamestate != wipegamestate) + { + wipe = true; + wipe_StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); + } + else + wipe = false; + + if (gamestate == GS_LEVEL && gametic) + HU_Erase(); + + // do buffered drawing + switch (gamestate) + { + case GS_LEVEL: + if (!gametic) + break; + if (automapactive) + AM_Drawer (); + if (wipe || (viewheight != 200 && fullscreen) ) + redrawsbar = true; + if (inhelpscreensstate && !inhelpscreens) + redrawsbar = true; // just put away the help screen + ST_Drawer (viewheight == 200, redrawsbar ); + fullscreen = viewheight == 200; + break; + + case GS_INTERMISSION: + WI_Drawer (); + break; + + case GS_FINALE: + F_Drawer (); + break; + + case GS_DEMOSCREEN: + D_PageDrawer (); + break; + } + + // draw buffered stuff to screen + I_UpdateNoBlit (); + + // draw the view directly + if (gamestate == GS_LEVEL && !automapactive && gametic) + R_RenderPlayerView (&players[displayplayer]); + + if (gamestate == GS_LEVEL && gametic) + HU_Drawer (); + + // clean up border stuff + if (gamestate != oldgamestate && gamestate != GS_LEVEL) + I_SetPalette (W_CacheLumpName ("PLAYPAL",PU_CACHE)); + + // see if the border needs to be initially drawn + if (gamestate == GS_LEVEL && oldgamestate != GS_LEVEL) + { + viewactivestate = false; // view was not active + R_FillBackScreen (); // draw the pattern into the back screen + } + + // see if the border needs to be updated to the screen + if (gamestate == GS_LEVEL && !automapactive && scaledviewwidth != 320) + { + if (menuactive || menuactivestate || !viewactivestate) + borderdrawcount = 3; + if (borderdrawcount) + { + R_DrawViewBorder (); // erase old menu stuff + borderdrawcount--; + } + + } + + menuactivestate = menuactive; + viewactivestate = viewactive; + inhelpscreensstate = inhelpscreens; + oldgamestate = wipegamestate = gamestate; + + // draw pause pic + if (paused) + { + if (automapactive) + y = 4; + else + y = viewwindowy+4; + V_DrawPatchDirect(viewwindowx+(scaledviewwidth-68)/2, + y,0,W_CacheLumpName ("M_PAUSE", PU_CACHE)); + } + + + // menus go directly to the screen + M_Drawer (); // menu is drawn even on top of everything + NetUpdate (); // send out any new accumulation + + + // normal update + if (!wipe) + { + I_FinishUpdate (); // page flip or blit buffer + return; + } + + // wipe update + wipe_EndScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); + + wipestart = I_GetTime () - 1; + + do + { + do + { + nowtime = I_GetTime (); + tics = nowtime - wipestart; + } while (!tics); + wipestart = nowtime; + done = wipe_ScreenWipe(wipe_Melt + , 0, 0, SCREENWIDTH, SCREENHEIGHT, tics); + I_UpdateNoBlit (); + M_Drawer (); // menu is drawn even on top of wipes + I_FinishUpdate (); // page flip or blit buffer + } while (!done); +} + + + +// +// D_DoomLoop +// +extern boolean demorecording; + +void D_DoomLoop (void) +{ + if (demorecording) + G_BeginRecording (); + + if (M_CheckParm ("-debugfile")) + { + char filename[20]; + sprintf (filename,"debug%i.txt",consoleplayer); + printf ("debug output to: %s\n",filename); + 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 (); + M_Ticker (); + G_Ticker (); + gametic++; + maketic++; + } + else + { + TryRunTics (); // will run at least one tic + } + + S_UpdateSounds (players[consoleplayer].mo);// move positional sounds + + // Update display, next frame, with current state. + D_Display (); + +#ifndef SNDSERV + // Sound mixing for the buffer is snychronous. + I_UpdateSound(); +#endif + // Synchronous sound output is explicitly called. +#ifndef SNDINTR + // Update sound output. + I_SubmitSound(); +#endif + } +} + + + +// +// DEMO LOOP +// +int demosequence; +int pagetic; +char *pagename; + + +// +// D_PageTicker +// Handles timing for warped projection +// +void D_PageTicker (void) +{ + if (--pagetic < 0) + D_AdvanceDemo (); +} + + + +// +// D_PageDrawer +// +void D_PageDrawer (void) +{ + V_DrawPatch (0,0, 0, W_CacheLumpName(pagename, PU_CACHE)); +} + + +// +// D_AdvanceDemo +// Called after each demo or intro demosequence finishes +// +void D_AdvanceDemo (void) +{ + advancedemo = true; +} + + +// +// This cycles through the demo sequences. +// FIXME - version dependend demo numbers? +// + void D_DoAdvanceDemo (void) +{ + players[consoleplayer].playerstate = PST_LIVE; // not reborn + advancedemo = false; + usergame = false; // no save / end game here + paused = false; + gameaction = ga_nothing; + + if ( gamemode == retail ) + demosequence = (demosequence+1)%7; + else + demosequence = (demosequence+1)%6; + + switch (demosequence) + { + case 0: + if ( gamemode == commercial ) + pagetic = 35 * 11; + else + pagetic = 170; + gamestate = GS_DEMOSCREEN; + pagename = "TITLEPIC"; + if ( gamemode == commercial ) + S_StartMusic(mus_dm2ttl); + else + S_StartMusic (mus_intro); + break; + case 1: + G_DeferedPlayDemo ("demo1"); + break; + case 2: + pagetic = 200; + gamestate = GS_DEMOSCREEN; + pagename = "CREDIT"; + break; + case 3: + G_DeferedPlayDemo ("demo2"); + break; + case 4: + gamestate = GS_DEMOSCREEN; + if ( gamemode == commercial) + { + pagetic = 35 * 11; + pagename = "TITLEPIC"; + S_StartMusic(mus_dm2ttl); + } + else + { + pagetic = 200; + + if ( gamemode == retail ) + pagename = "CREDIT"; + else + pagename = "HELP2"; + } + break; + case 5: + G_DeferedPlayDemo ("demo3"); + break; + // THE DEFINITIVE DOOM Special Edition demo + case 6: + G_DeferedPlayDemo ("demo4"); + break; + } +} + + + +// +// D_StartTitle +// +void D_StartTitle (void) +{ + gameaction = ga_nothing; + demosequence = -1; + D_AdvanceDemo (); +} + + + + +// print title for every printed line +char title[128]; + + + +// +// D_AddFile +// +void D_AddFile (char *file) +{ + int numwadfiles; + char *newfile; + + for (numwadfiles = 0 ; wadfiles[numwadfiles] ; numwadfiles++) + ; + + newfile = malloc (strlen(file)+1); + strcpy (newfile, file); + + wadfiles[numwadfiles] = newfile; +} + +// +// IdentifyVersion +// Checks availability of IWAD files by name, +// to determine whether registered/commercial features +// should be executed (notably loading PWAD's). +// +void IdentifyVersion (void) +{ + + char* doom1wad; + char* doomwad; + char* doomuwad; + char* doom2wad; + + char* doom2fwad; + char* plutoniawad; + char* tntwad; + +#ifdef NORMALUNIX + char *home; + char *doomwaddir; + doomwaddir = getenv("DOOMWADDIR"); + if (!doomwaddir) + doomwaddir = "."; + + // Commercial. + doom2wad = malloc(strlen(doomwaddir)+1+9+1); + sprintf(doom2wad, "%s/doom2.wad", doomwaddir); + + // Retail. + doomuwad = malloc(strlen(doomwaddir)+1+8+1); + sprintf(doomuwad, "%s/doomu.wad", doomwaddir); + + // Registered. + doomwad = malloc(strlen(doomwaddir)+1+8+1); + sprintf(doomwad, "%s/doom.wad", doomwaddir); + + // Shareware. + doom1wad = malloc(strlen(doomwaddir)+1+9+1); + sprintf(doom1wad, "%s/doom1.wad", doomwaddir); + + // Bug, dear Shawn. + // Insufficient malloc, caused spurious realloc errors. + plutoniawad = malloc(strlen(doomwaddir)+1+/*9*/12+1); + sprintf(plutoniawad, "%s/plutonia.wad", doomwaddir); + + tntwad = malloc(strlen(doomwaddir)+1+9+1); + sprintf(tntwad, "%s/tnt.wad", doomwaddir); + + + // French stuff. + doom2fwad = malloc(strlen(doomwaddir)+1+10+1); + sprintf(doom2fwad, "%s/doom2f.wad", doomwaddir); + + home = getenv("HOME"); + if (!home) + I_Error("Please set $HOME to your home directory"); + sprintf(basedefault, "%s/.doomrc", home); +#endif + + if (M_CheckParm ("-shdev")) + { + gamemode = shareware; + devparm = true; + D_AddFile (DEVDATA"doom1.wad"); + D_AddFile (DEVMAPS"data_se/texture1.lmp"); + D_AddFile (DEVMAPS"data_se/pnames.lmp"); + strcpy (basedefault,DEVDATA"default.cfg"); + return; + } + + if (M_CheckParm ("-regdev")) + { + gamemode = registered; + devparm = true; + D_AddFile (DEVDATA"doom.wad"); + D_AddFile (DEVMAPS"data_se/texture1.lmp"); + D_AddFile (DEVMAPS"data_se/texture2.lmp"); + D_AddFile (DEVMAPS"data_se/pnames.lmp"); + strcpy (basedefault,DEVDATA"default.cfg"); + return; + } + + if (M_CheckParm ("-comdev")) + { + gamemode = commercial; + devparm = true; + /* I don't bother + if(plutonia) + D_AddFile (DEVDATA"plutonia.wad"); + else if(tnt) + D_AddFile (DEVDATA"tnt.wad"); + else*/ + D_AddFile (DEVDATA"doom2.wad"); + + D_AddFile (DEVMAPS"cdata/texture1.lmp"); + D_AddFile (DEVMAPS"cdata/pnames.lmp"); + strcpy (basedefault,DEVDATA"default.cfg"); + return; + } + + if ( !access (doom2fwad,R_OK) ) + { + gamemode = commercial; + // C'est ridicule! + // Let's handle languages in config files, okay? + language = french; + printf("French version\n"); + D_AddFile (doom2fwad); + return; + } + + if ( !access (doom2wad,R_OK) ) + { + gamemode = commercial; + D_AddFile (doom2wad); + return; + } + + if ( !access (plutoniawad, R_OK ) ) + { + gamemode = commercial; + D_AddFile (plutoniawad); + return; + } + + if ( !access ( tntwad, R_OK ) ) + { + gamemode = commercial; + D_AddFile (tntwad); + return; + } + + if ( !access (doomuwad,R_OK) ) + { + gamemode = retail; + D_AddFile (doomuwad); + return; + } + + if ( !access (doomwad,R_OK) ) + { + gamemode = registered; + D_AddFile (doomwad); + return; + } + + if ( !access (doom1wad,R_OK) ) + { + gamemode = shareware; + D_AddFile (doom1wad); + return; + } + + printf("Game mode indeterminate.\n"); + gamemode = indetermined; + + // We don't abort. Let's see what the PWAD contains. + //exit(1); + //I_Error ("Game mode indeterminate\n"); +} + +// +// Find a Response File +// +void FindResponseFile (void) +{ + int i; +#define MAXARGVS 100 + + for (i = 1;i < myargc;i++) + if (myargv[i][0] == '@') + { + FILE * handle; + int size; + int k; + int index; + int indexinfile; + char *infile; + char *file; + char *moreargs[20]; + char *firstargv; + + // READ THE RESPONSE FILE INTO MEMORY + handle = fopen (&myargv[i][1],"rb"); + if (!handle) + { + printf ("\nNo such response file!"); + exit(1); + } + printf("Found response file %s!\n",&myargv[i][1]); + fseek (handle,0,SEEK_END); + size = ftell(handle); + fseek (handle,0,SEEK_SET); + file = malloc (size); + fread (file,size,1,handle); + fclose (handle); + + // KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG + for (index = 0,k = i+1; k < myargc; k++) + moreargs[index++] = myargv[k]; + + firstargv = myargv[0]; + myargv = malloc(sizeof(char *)*MAXARGVS); + memset(myargv,0,sizeof(char *)*MAXARGVS); + myargv[0] = firstargv; + + infile = file; + indexinfile = k = 0; + indexinfile++; // SKIP PAST ARGV[0] (KEEP IT) + do + { + myargv[indexinfile++] = infile+k; + while(k < size && + ((*(infile+k)>= ' '+1) && (*(infile+k)<='z'))) + k++; + *(infile+k) = 0; + while(k < size && + ((*(infile+k)<= ' ') || (*(infile+k)>'z'))) + k++; + } while(k < size); + + for (k = 0;k < index;k++) + myargv[indexinfile++] = moreargs[k]; + myargc = indexinfile; + + // DISPLAY ARGS + printf("%d command-line args:\n",myargc); + for (k=1;k 400) + scale = 400; + printf ("turbo scale: %i%%\n",scale); + forwardmove[0] = forwardmove[0]*scale/100; + forwardmove[1] = forwardmove[1]*scale/100; + sidemove[0] = sidemove[0]*scale/100; + sidemove[1] = sidemove[1]*scale/100; + } + + // add any files specified on the command line with -file wadfile + // to the wad list + // + // convenience hack to allow -wart e m to add a wad file + // prepend a tilde to the filename so wadfile will be reloadable + p = M_CheckParm ("-wart"); + if (p) + { + myargv[p][4] = 'p'; // big hack, change to -warp + + // Map name handling. + switch (gamemode ) + { + case shareware: + case retail: + case registered: + sprintf (file,"~"DEVMAPS"E%cM%c.wad", + myargv[p+1][0], myargv[p+2][0]); + printf("Warping to Episode %s, Map %s.\n", + myargv[p+1],myargv[p+2]); + break; + + case commercial: + default: + p = atoi (myargv[p+1]); + if (p<10) + sprintf (file,"~"DEVMAPS"cdata/map0%i.wad", p); + else + sprintf (file,"~"DEVMAPS"cdata/map%i.wad", p); + break; + } + D_AddFile (file); + } + + p = M_CheckParm ("-file"); + if (p) + { + // the parms after p are wadfile/lump names, + // until end of parms or another - preceded parm + modifiedgame = true; // homebrew levels + while (++p != myargc && myargv[p][0] != '-') + D_AddFile (myargv[p]); + } + + p = M_CheckParm ("-playdemo"); + + if (!p) + p = M_CheckParm ("-timedemo"); + + if (p && p < myargc-1) + { + sprintf (file,"%s.lmp", myargv[p+1]); + D_AddFile (file); + printf("Playing demo %s.lmp.\n",myargv[p+1]); + } + + // get skill / episode / map from parms + startskill = sk_medium; + startepisode = 1; + startmap = 1; + autostart = false; + + + 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 ("-timer"); + if (p && p < myargc-1 && deathmatch) + { + int time; + time = atoi(myargv[p+1]); + printf("Levels will end after %d minute",time); + if (time>1) + printf("s"); + printf(".\n"); + } + + p = M_CheckParm ("-avg"); + if (p && p < myargc-1 && deathmatch) + printf("Austin Virtual Gaming: Levels will end after 20 minutes\n"); + + p = M_CheckParm ("-warp"); + if (p && p < myargc-1) + { + if (gamemode == commercial) + startmap = atoi (myargv[p+1]); + else + { + 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 ("M_LoadDefaults: Load system defaults.\n"); + M_LoadDefaults (); // load before initing other systems + + printf ("Z_Init: Init zone memory allocation daemon. \n"); + Z_Init (); + + printf ("W_Init: Init WADfiles.\n"); + W_InitMultipleFiles (wadfiles); + + + // Check for -file in shareware + if (modifiedgame) + { + // These are the lumps that will be checked in IWAD, + // if any one is not present, execution will be aborted. + char name[23][8]= + { + "e2m1","e2m2","e2m3","e2m4","e2m5","e2m6","e2m7","e2m8","e2m9", + "e3m1","e3m3","e3m3","e3m4","e3m5","e3m6","e3m7","e3m8","e3m9", + "dphoof","bfgga0","heada1","cybra1","spida1d1" + }; + int i; + + if ( gamemode == shareware) + I_Error("\nYou cannot -file with the shareware " + "version. Register!"); + + // Check for fake IWAD with right name, + // but w/o all the lumps of the registered version. + if (gamemode == registered) + for (i = 0;i < 23; i++) + if (W_CheckNumForName(name[i])<0) + I_Error("\nThis is not the registered version."); + } + + // Iff additonal PWAD files are used, print modified banner + if (modifiedgame) + { + /*m*/printf ( + "===========================================================================\n" + "ATTENTION: This version of DOOM has been modified. If you would like to\n" + "get a copy of the original game, call 1-800-IDGAMES or see the readme file.\n" + " You will not receive technical support for modified games.\n" + " press enter to continue\n" + "===========================================================================\n" + ); + getchar (); + } + + + // Check and print which version is executed. + switch ( gamemode ) + { + case shareware: + case indetermined: + printf ( + "===========================================================================\n" + " Shareware!\n" + "===========================================================================\n" + ); + break; + case registered: + case retail: + case commercial: + printf ( + "===========================================================================\n" + " Commercial product - do not distribute!\n" + " Please report software piracy to the SPA: 1-800-388-PIR8\n" + "===========================================================================\n" + ); + break; + + default: + // Ouch. + break; + } + + printf ("M_Init: Init miscellaneous info.\n"); + M_Init (); + + printf ("R_Init: Init DOOM refresh daemon - "); + R_Init (); + + printf ("\nP_Init: Init Playloop state.\n"); + P_Init (); + + printf ("I_Init: Setting up machine state.\n"); + I_Init (); + + printf ("D_CheckNetGame: Checking network game status.\n"); + D_CheckNetGame (); + + printf ("S_Init: Setting up sound.\n"); + S_Init (snd_SfxVolume /* *8 */, snd_MusicVolume /* *8*/ ); + + printf ("HU_Init: Setting up heads up display.\n"); + HU_Init (); + + printf ("ST_Init: Init status bar.\n"); + ST_Init (); + + // check for a driver that wants intermission stats + p = M_CheckParm ("-statcopy"); + if (p && p