// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id:$ // // 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:$ // // 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,v 1.8 1997/02/03 22:45:09 b1 Exp $"; #define BGCOLOR 7 #define FGCOLOR 8 #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; 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 (); 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 = I_AllocLow (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) { I_sprintf(basedefault, ".doomrc"); 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 (I_HaveFile("doom2.wad") ) { gamemode = commercial; D_AddFile ("doom2.wad"); return; } if (I_HaveFile("plutonia.wad") ) { gamemode = commercial; D_AddFile ("plutonia.wad"); return; } if (I_HaveFile("tnt.wad") ) { gamemode = commercial; D_AddFile ("tnt.wad"); return; } if (I_HaveFile("doomu.wad") ) { gamemode = retail; D_AddFile ("doomu.wad"); return; } if (I_HaveFile("doom.wad") ) { gamemode = registered; D_AddFile ("doom.wad"); return; } if (I_HaveFile("doom1.wad") ) { gamemode = shareware; D_AddFile ("doom1.wad"); return; } I_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"); } // // D_DoomMain // void D_DoomMain (void) { int p; char file[256]; IdentifyVersion (); modifiedgame = false; nomonsters = M_CheckParm ("-nomonsters"); respawnparm = M_CheckParm ("-respawn"); fastparm = M_CheckParm ("-fast"); devparm = M_CheckParm ("-devparm"); if (M_CheckParm ("-altdeath")) deathmatch = 2; else if (M_CheckParm ("-deathmatch")) deathmatch = 1; switch ( gamemode ) { case retail: I_sprintf (title, " " "The Ultimate DOOM Startup v%i.%i" " ", VERSION/100,VERSION%100); break; case shareware: I_sprintf (title, " " "DOOM Shareware Startup v%i.%i" " ", VERSION/100,VERSION%100); break; case registered: I_sprintf (title, " " "DOOM Registered Startup v%i.%i" " ", VERSION/100,VERSION%100); break; case commercial: I_sprintf (title, " " "DOOM 2: Hell on Earth v%i.%i" " ", VERSION/100,VERSION%100); break; /*FIXME case pack_plut: I_sprintf (title, " " "DOOM 2: Plutonia Experiment v%i.%i" " ", VERSION/100,VERSION%100); break; case pack_tnt: I_sprintf (title, " " "DOOM 2: TNT - Evilution v%i.%i" " ", VERSION/100,VERSION%100); break; */ default: I_sprintf (title, " " "Public DOOM - v%i.%i" " ", VERSION/100,VERSION%100); break; } I_printf ("%s\n",title); if (devparm) I_printf(D_DEVSTR); // turbo option if ( (p=M_CheckParm ("-turbo")) ) { int scale = 200; extern int forwardmove[2]; extern int sidemove[2]; if (p 400) scale = 400; I_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: I_sprintf (file,"~"DEVMAPS"E%cM%c.wad", myargv[p+1][0], myargv[p+2][0]); I_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) I_sprintf (file,"~"DEVMAPS"cdata/map0%i.wad", p); else I_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) { I_sprintf (file,"%s.lmp", myargv[p+1]); D_AddFile (file); I_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]); I_printf("Levels will end after %d minute",time); if (time>1) I_printf("s"); I_printf(".\n"); } p = M_CheckParm ("-avg"); if (p && p < myargc-1 && deathmatch) I_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 I_printf ("V_Init: allocate screens.\n"); V_Init (); I_printf ("M_LoadDefaults: Load system defaults.\n"); M_LoadDefaults (); // load before initing other systems I_printf ("Z_Init: Init zone memory allocation daemon. \n"); Z_Init (); I_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*/I_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" ); } // Check and print which version is executed. switch ( gamemode ) { case shareware: case indetermined: I_printf ( "===========================================================================\n" " Shareware!\n" "===========================================================================\n" ); break; case registered: case retail: case commercial: I_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; } I_printf ("M_Init: Init miscellaneous info.\n"); M_Init (); I_printf ("R_Init: Init DOOM refresh daemon - "); R_Init (); I_printf ("\nP_Init: Init Playloop state.\n"); P_Init (); I_printf ("I_Init: Setting up machine state.\n"); I_Init (); I_printf ("D_CheckNetGame: Checking network game status.\n"); D_CheckNetGame (); I_printf ("S_Init: Setting up sound.\n"); S_Init (snd_SfxVolume /* *8 */, snd_MusicVolume /* *8*/ ); I_printf ("HU_Init: Setting up heads up display.\n"); HU_Init (); I_printf ("ST_Init: Init status bar.\n"); ST_Init (); // check for a driver that wants intermission stats p = M_CheckParm ("-statcopy"); if (p && p