gzdoom/code/D_main.c
1999-02-21 00:00:00 +00:00

1388 lines
31 KiB
C

// 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.
//
//-----------------------------------------------------------------------------
#ifdef _WIN32
#include <direct.h>
#define mkdir(a,b) mkdir (a)
#else
#include <sys/stat.h>
#endif
#include "m_alloc.h"
#include "minilzo.h"
#include "doomdef.h"
#include "doomstat.h"
#include "dstrings.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 "c_consol.h"
#include "c_cmds.h"
#include "c_dispch.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 "r_sky.h"
#include "d_main.h"
#include "d_dehack.h"
#include "cmdlib.h"
#include "s_sound.h"
#include "m_swap.h"
cvar_t *fraglimit;
cvar_t *timelimit;
cvar_t *turbo;
extern void TurboCallback (cvar_t *);
//
// 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);
wadlist_t *wadfiles; // [RH] remove limit on # of loaded wads
wadlist_t **wadtail = &wadfiles;
BOOL devparm; // started game with -devparm
int NoWipe; // [RH] Allow wipe? (Needs to be set each time)
BOOL drone;
BOOL singletics = false; // debug flag to cancel adaptiveness
char *D_DrawIcon; // [RH] Patch name of icon to draw on next refresh
extern BOOL inhelpscreens;
char startmap[8];
BOOL autostart;
FILE* debugfile;
BOOL advancedemo;
char wadfile[1024]; // primary wad 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 (const event_t* ev)
{
events[eventhead] = *ev;
eventhead = (++eventhead)&(MAXEVENTS-1);
}
//
// D_ProcessEvents
// Send all the events of the given timestamp down the responder chain
//
// [RH] Stuff for screenmode testing
extern int testingmode;
extern void M_RestoreMode (void);
void D_ProcessEvents (void)
{
event_t *ev;
// [RH] If testing mode, do not accept input until test is over
if (testingmode) {
if (testingmode <= I_GetTime()) {
M_RestoreMode ();
}
return;
}
for ( ; eventtail != eventhead ; eventtail = (++eventtail)&(MAXEVENTS-1) )
{
ev = &events[eventtail];
if (C_Responder (ev))
continue; // console ate the event
if (M_Responder (ev))
continue; // menu ate the event
G_Responder (ev);
}
}
// [RH] Each time dmflags is changed, this function is called and
// transforms it into an integer so that we don't need to make
// the conversion each time we check its value.
void DMFlagsCallback (cvar_t *var)
{
dmflags = (int)var->value;
// In case DF_NO_FREELOOK was changed, reinitialize the sky
// map. (If no freelook, then no need to stretch the sky.)
if (textureheight)
R_InitSkyMap (r_stretchsky);
if (dmflags & DF_NO_FREELOOK)
AddCommandString ("centerview");
}
//
// 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 BOOL setsizeneeded, setmodeneeded;
extern int NewWidth, NewHeight, NewID, DisplayID;
extern cvar_t *st_scale;
void R_ExecuteSetViewSize (void);
void D_Display (void)
{
BOOL wipe;
if (nodrawers)
return; // for comparative timing / profiling
// [RH] change the screen mode if needed
if (setmodeneeded) {
int oldwidth = screen.width;
int oldheight = screen.height;
int oldid = DisplayID;
// Change screen mode.
if (!V_SetResolution (NewWidth, NewHeight, NewID))
if (!V_SetResolution (oldwidth, oldheight, oldid))
I_FatalError ("Could not change screen mode");
// Recalculate various view parameters.
setsizeneeded = true;
// Trick status bar into rethinking its position
SetCVarFloat (st_scale, st_scale->value);
SB_state = -1;
// Refresh the console.
C_NewModeAdjust ();
}
// change the view size if needed
if (setsizeneeded)
{
R_ExecuteSetViewSize ();
setmodeneeded = false;
}
I_BeginUpdate ();
// [RH] Allow temporarily disabling wipes
if (NoWipe)
{
BorderNeedRefresh = true;
NoWipe--;
wipe = false;
wipegamestate = gamestate;
}
else if (gamestate != wipegamestate && gamestate != GS_FULLCONSOLE)
{
// save the current screen if about to wipe
BorderNeedRefresh = true;
wipe = true;
wipe_StartScreen(0, 0, screen.width, screen.height);
wipegamestate = gamestate;
}
else
{
wipe = false;
}
switch (gamestate) {
case GS_FULLCONSOLE:
C_DrawConsole ();
M_Drawer ();
I_FinishUpdate ();
return;
case GS_LEVEL:
if (!gametic)
break;
if (viewactive)
R_RenderPlayerView (&players[consoleplayer]);
if (automapactive)
AM_Drawer ();
C_DrawMid ();
ST_Drawer ();
CT_Drawer ();
break;
case GS_INTERMISSION:
WI_Drawer ();
break;
case GS_FINALE:
F_Drawer ();
break;
case GS_DEMOSCREEN:
D_PageDrawer ();
break;
}
// draw pause pic
if (paused && !menuactive)
{
patch_t *pause = W_CacheLumpName ("M_PAUSE", PU_CACHE);
int y;
y = (automapactive && !viewactive) ? 4 : viewwindowy + 4;
V_DrawPatchCleanNoMove((screen.width-(pause->width)*CleanXfac)/2,y,&screen,pause);
}
// [RH] Draw icon, if any
if (D_DrawIcon) {
int lump = W_CheckNumForName (D_DrawIcon);
D_DrawIcon = NULL;
if (lump >= 0) {
patch_t *p = W_CacheLumpNum (lump, PU_CACHE);
V_DrawPatchIndirect (160-SHORT(p->width)/2, 100-SHORT(p->height)/2,
&screen, p);
}
NoWipe = 10;
}
NetUpdate (); // send out any new accumulation
if (!wipe) {
// normal update
C_DrawConsole (); // draw console
M_Drawer (); // menu is drawn even on top of everything
I_FinishUpdate (); // page flip or blit buffer
} else {
// wipe update
int wipestart, nowtime, tics;
BOOL done;
wipe_EndScreen(0, 0, screen.width, screen.height);
I_FinishUpdateNoBlit ();
wipestart = I_GetTime () - 1;
do
{
do
{
nowtime = I_GetTime ();
tics = nowtime - wipestart;
} while (!tics);
wipestart = nowtime;
I_BeginUpdate ();
done = wipe_ScreenWipe(wipe_Melt,
0, 0, screen.width, screen.height, tics);
C_DrawConsole ();
M_Drawer (); // menu is drawn even on top of wipes
I_FinishUpdate (); // page flip or blit buffer
} while (!done);
}
}
//
// [RH] D_ErrorLoop
//
char errortext[2048];
jmp_buf errorjmp;
BOOL errorjmpable;
extern BOOL gameisdead;
static void STACK_ARGS godead (void)
{
gameisdead = true;
}
void D_ErrorLoop (void)
{
int i;
if (gameaction != ga_nothing) {
gamestate = GS_FULLCONSOLE;
C_FullConsole ();
}
atexit (godead);
while (1) {
if (setjmp (errorjmp)) {
errorjmpable = false;
if (errortext[0]) {
Printf (PRINT_HIGH, "%s\n", errortext);
errortext[0] = 0;
D_QuitNetGame ();
netgame = false;
singletics = false;
if (demorecording || demoplayback)
G_CheckDemoStatus ();
for (i = 1; i < MAXPLAYERS; i++)
playeringame[i] = 0;
consoleplayer = 0;
playeringame[0] = 1;
players[0].playerstate = PST_LIVE;
gameaction = ga_fullconsole;
}
}
errorjmpable = true;
D_DoomLoop (); // never returns
}
}
//
// D_DoomLoop
//
extern BOOL demorecording;
void D_DoomLoop (void)
{
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 ();
C_Ticker ();
M_Ticker ();
G_Ticker ();
gametic++;
maketic++;
Net_NewMakeTic ();
}
else
{
TryRunTics (); // will run at least one tic
}
// [RH] Use the consoleplayer's camera to update sounds
S_UpdateSounds (players[consoleplayer].camera); // move positional sounds
// Update display, next frame, with current state.
D_Display ();
}
}
//
// 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_DrawPatchIndirect (0,0, &screen, 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?
//
// [RH] If M_DemoNoPlay is true, then any
// demos are skipped.
extern BOOL M_DemoNoPlay;
void D_DoAdvanceDemo (void)
{
static char *pages[2][3] = {
{ "CREDIT", "TITLEPIC", "HELP2" },
{ "CREDIT", "TITLEPIC", "CREDIT" }
};
static char demoname[8] = "DEMO1";
static int democount = 0;
static int pagecount;
char **page;
players[consoleplayer].playerstate = PST_LIVE; // not reborn
advancedemo = false;
usergame = false; // no save / end game here
paused = false;
gameaction = ga_nothing;
if (gamemode == retail || gamemode == commercial)
{
page = pages[1];
}
else
{
page = pages[0];
}
switch (demosequence)
{
case 1:
if (!M_DemoNoPlay)
{
BorderNeedRefresh = true;
democount++;
sprintf (demoname + 4, "%d", democount);
if (W_CheckNumForName (demoname) < 0)
{
demosequence = 0;
democount = 1;
// falls through to case 0 below
}
else
{
G_DeferedPlayDemo (demoname);
demosequence = 2;
break;
}
}
default:
case 0:
gamestate = GS_DEMOSCREEN;
pagename = "TITLEPIC";
if (gamemode == commercial)
{
pagetic = TICRATE * 11;
if (HexenHack)
{ // [RH] :-)
S_StartMusic ("hexen");
}
else
{
S_StartMusic ("D_DM2TTL");
}
}
else
{
pagetic = TICRATE * 5;
S_StartMusic ("D_INTRO");
}
demosequence = 1;
pagecount = 0;
C_HideConsole ();
break;
case 2:
pagetic = (TICRATE * 200) / 35;
gamestate = GS_DEMOSCREEN;
pagename = page[pagecount];
pagecount = (pagecount+1) % 3;
demosequence = 1;
break;
}
}
//
// D_StartTitle
//
void D_StartTitle (void)
{
gameaction = ga_nothing;
demosequence = -1;
D_AdvanceDemo ();
}
//
// [RH] Quit the current game and go to fullscreen console
//
void Cmd_Endgame (player_t *plyr, int argc, char **argv)
{
if (!netgame) {
gameaction = ga_fullconsole;
demosequence = -1;
}
}
//
// D_AddFile
//
void D_AddFile (char *file)
{
wadlist_t *wad = Z_Malloc (sizeof(*wad) + strlen(file), PU_STATIC, 0);
*wadtail = wad;
wad->next = NULL;
strcpy (wad->name, file);
wadtail = &wad->next;
}
//
// IdentifyVersion
// Checks availability of IWAD files by name,
// to determine whether registered/commercial features
// should be executed (notably loading PWAD's).
// [RH] Rewritten to recognize the IWAD by its contents, not its name.
//
static const char *IdentifyVersion (void)
{
const char *titlestring = "Public DOOM - ";
static const char doomwadnames[][13] = {
"doom2f.wad",
"doom2.wad",
"plutonia.wad",
"tnt.wad",
"doomu.wad", // Hack from original Linux version. Not necessary, but I threw it in anyway.
"doom.wad",
"doom1.wad",
""
};
int i;
char *doomwaddir;
char iwad[256];
int iwadindex;
doomwaddir = progdir;
// Search for a pre-defined IWAD from the list above
for (i = 0; doomwadnames[i][0]; i++) {
sprintf (iwad, "%s%s", doomwaddir, doomwadnames[i]);
if (FileExists (iwad))
break;
}
if (!doomwadnames[i][0])
iwad[0] = 0;
// See if the user wants to use a different iwad instead
iwadindex = M_CheckParm ("-iwad");
if (iwadindex && iwadindex < myargc - 1) {
char *custwad = Z_Malloc(strlen(myargv[++iwadindex]) + 5, PU_STATIC, 0);
strcpy (custwad, myargv[iwadindex]);
FixPathSeperator (custwad);
DefaultExtension (custwad, ".wad");
if (FileExists (custwad))
strcpy (iwad, custwad);
Z_Free (custwad);
}
// Now scan the contents of the IWAD to determine which one it is
if (iwad[0]) {
static const char checklumps[8][8] = {
"E1M1", "E2M1", "E4M1", "MAP01",
"ANIMDEFS", "FINAL2", "REDTNT2", "CAMO1"
};
int lumpsfound[8];
wadinfo_t header;
FILE *f;
memset (lumpsfound, 0, sizeof(lumpsfound));
if ( (f = fopen (iwad, "rb")) ) {
fread (&header, sizeof(header), 1, f);
if (header.identification == IWAD_ID ||
header.identification == PWAD_ID) {
header.numlumps = LONG(header.numlumps);
if (0 == fseek (f, LONG(header.infotableofs), SEEK_SET)) {
for (i = 0; i < header.numlumps; i++) {
filelump_t lump;
int j;
if (0 == fread (&lump, sizeof(lump), 1, f))
break;
for (j = 0; j < 8; j++)
if (!strnicmp (lump.name, checklumps[j], 8))
lumpsfound[j]++;
}
}
}
fclose (f);
}
gamemode = undetermined;
if (lumpsfound[3]) {
gamemode = commercial;
if (lumpsfound[6]) {
gamemission = pack_tnt;
titlestring = "DOOM 2: TNT - Evilution";
} else if (lumpsfound[7]) {
gamemission = pack_plut;
titlestring = "DOOM 2: Plutonia Experiment";
} else {
gamemission = doom2;
if (lumpsfound[4])
titlestring = "Hexen (But It Won't Work)";
else
titlestring = "DOOM 2: Hell on Earth";
}
} else if (lumpsfound[0]) {
gamemission = doom;
if (lumpsfound[1]) {
if (lumpsfound[2]) {
gamemode = retail;
titlestring = "The Ultimate DOOM";
} else {
gamemode = registered;
titlestring = "DOOM Registered";
}
} else {
gamemode = shareware;
titlestring = "DOOM Shareware";
}
if (lumpsfound[5])
titlestring = "Heretic (But It Won't Work)";
}
}
if (gamemode == undetermined)
Printf (PRINT_HIGH, "Game mode indeterminate.\n");
if (iwad[0])
D_AddFile (iwad);
return titlestring;
}
//
// Find a Response File
//
void FindResponseFile (void)
{
#define MAXARGVS 100
int i;
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)
I_FatalError ("\nNo such response file!");
Printf (PRINT_HIGH, "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 (PRINT_HIGH, "%d command-line args:\n",myargc);
for (k = 1; k < myargc; k++)
Printf (PRINT_HIGH, "%s\n",myargv[k]);
break;
}
}
// [RH] Copied this from BOOM 2.02:
//
// DoLooseFiles
//
// Take any file names on the command line before the first switch parm
// and insert the appropriate -file, -deh or -playdemo switch in front
// of them.
//
// Note that more than one -file, etc. entry on the command line won't
// work, so we have to go get all the valid ones if any that show up
// after the loose ones. This means that boom fred.wad -file wilma
// will still load fred.wad and wilma.wad, in that order.
// The response file code kludges up its own version of myargv[] and
// unfortunately we have to do the same here because that kludge only
// happens if there _is_ a response file. Truth is, it's more likely
// that there will be a need to do one or the other so it probably
// isn't important. We'll point off to the original argv[], or the
// area allocated in FindResponseFile, or our own areas from copystrings.
//
static void DoLooseFiles (void)
{
char *wads[MAXARGVS]; // store the respective loose filenames
char *lmps[MAXARGVS];
char *dehs[MAXARGVS];
int wadcount = 0; // count the loose filenames
int lmpcount = 0;
int dehcount = 0;
int i,j,p;
char **tmyargv; // use these to recreate the argv array
int tmyargc;
for (i=1; i < myargc; i++)
{
if (*myargv[i] == '-' || *myargv[i] == '+') break; // quit at first switch
// so now we must have a loose file. Find out what kind and store it.
j = strlen(myargv[i]);
if (!stricmp(&myargv[i][j-4], ".wad"))
wads[wadcount++] = copystring (myargv[i]);
if (!stricmp(&myargv[i][j-4], ".lmp"))
lmps[lmpcount++] = copystring (myargv[i]);
if (!stricmp(&myargv[i][j-4], ".deh"))
dehs[dehcount++] = copystring (myargv[i]);
if (!stricmp(&myargv[i][j-4], ".bex"))
dehs[dehcount++] = copystring (myargv[i]);
if (myargv[i][j-4] != '.') // assume wad if no extension
wads[wadcount++] = copystring (myargv[i]);
*myargv[i] = '\0'; // nuke that entry so it won't repeat later
}
// Now, if we didn't find any loose files, we can just leave.
if (wadcount + lmpcount + dehcount == 0) return; // ******* early return ****
if ((p = M_CheckParm ("-file")))
{
*myargv[p] = '\0'; // nuke the entry
while (++p != myargc && *myargv[p] != '-' && *myargv[p] != '+')
{
wads[wadcount++] = copystring (myargv[p]);
*myargv[p] = '\0'; // null any we find and save
}
}
if ((p = M_CheckParm ("-deh")))
{
*myargv[p] = '\0'; // nuke the entry
while (++p != myargc && *myargv[p] != '-' && *myargv[p] != '+')
{
dehs[dehcount++] = copystring (myargv[p]);
*myargv[p] = '\0'; // null any we find and save
}
}
if ((p = M_CheckParm ("-playdemo")))
{
*myargv[p] = '\0'; // nuke the entry
while (++p != myargc && *myargv[p] != '-' && *myargv[p] != '+')
{
lmps[lmpcount++] = copystring (myargv[p]);
*myargv[p] = '\0'; // null any we find and save
}
}
// Now go back and redo the whole myargv array with our stuff in it.
// First, create a new myargv array to copy into
tmyargv = calloc(sizeof(char *), MAXARGVS);
tmyargv[0] = myargv[0]; // invocation
tmyargc = 1;
// put our stuff into it
if (wadcount > 0)
{
tmyargv[tmyargc++] = copystring ("-file"); // put the switch in
for (i=0;i<wadcount;)
tmyargv[tmyargc++] = wads[i++]; // allocated by copystring above
}
// for -deh
if (dehcount > 0)
{
tmyargv[tmyargc++] = copystring ("-deh");
for (i=0;i<dehcount;)
tmyargv[tmyargc++] = dehs[i++];
}
// for -playdemo
if (lmpcount > 0)
{
tmyargv[tmyargc++] = copystring ("-playdemo");
for (i=0;i<lmpcount;)
tmyargv[tmyargc++] = lmps[i++];
}
// then copy everything that's there now
for (i=1;i<myargc;i++)
{
if (*myargv[i]) // skip any null entries
tmyargv[tmyargc++] = myargv[i]; // pointers are still valid
}
// now make the global variables point to our array
myargv = tmyargv;
myargc = tmyargc;
}
extern thinker_t thinkercap;
void P_MobjThinker (mobj_t *mobj);
// [RH] This function is called whenever the lost soul translucency level
// changes. It searches through the entir world for any lost souls
// and sets their translucency levels as appropriate. New skulls are
// also set to spawn with the desired translucency.
void TransSoulsCallback (cvar_t *var)
{
thinker_t *currentthinker;
int floop;
if (var->value < 0.25) {
SetCVarFloat (var, 0.25);
} else if (var->value > 1) {
SetCVarFloat (var, 1);
} else {
floop = ((int)(var->value * 4) << MF_TRANSLUCSHIFT) & MF_TRANSLUCBITS;
mobjinfo[MT_SKULL].flags = (mobjinfo[MT_SKULL].flags & ~MF_TRANSLUCBITS) | floop;
// Find all the lost souls in the world and change them, also.
currentthinker = thinkercap.next;
if (!currentthinker)
return;
while (currentthinker != &thinkercap) {
if ( (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker)
&& ((mobj_t *)currentthinker)->type == MT_SKULL )
((mobj_t *)currentthinker)->flags =
(((mobj_t *)currentthinker)->flags & ~MF_TRANSLUCBITS) | floop;
currentthinker = currentthinker->next;
}
}
}
//
// D_DoomMain
//
void D_DoomMain (void)
{
int p, flags;
char file[256];
gamestate = GS_STARTUP;
// [RH] Initialize the minilzo package.
if (lzo_init () != LZO_E_OK)
I_FatalError ("Could not initialize LZO routines");
C_InstallCommands ();
M_LoadDefaults (); // load before initing other systems
// [RH] do all +set commands on the command line
C_ExecCmdLineParams (true);
// [RH] Set default cvar values. These will
// be overridden by the configfile.
C_SetCVars ();
{
cvar_t *var = cvar ("transsouls", "0.75", CVAR_ARCHIVE|CVAR_CALLBACK);
var->u.callback = TransSoulsCallback;
TransSoulsCallback (var);
}
FindResponseFile ();
DoLooseFiles(); // Ty 08/29/98 - handle "loose" files on command line
{
// [RH] Make sure zdoom.wad is always loaded,
// as it contains stuff we need.
char *zdoomwad = Z_Malloc (strlen (progdir) + 10, PU_STATIC, 0);
sprintf (zdoomwad, "%szdoom.wad", progdir);
D_AddFile (zdoomwad);
sprintf (zdoomwad, "%szvox.wad", progdir);
D_AddFile (zdoomwad);
Z_Free (zdoomwad);
}
I_SetTitleString (IdentifyVersion ());
// [RH] Add any .wad files in the skins directory
{
char skinname[256];
findstate_t findstate;
long handle;
int stuffstart;
stuffstart = sprintf (skinname, "%sskins/", progdir);
strcpy (skinname + stuffstart, "*.wad");
if ((handle = I_FindFirst (skinname, &findstate)) != -1)
{
do
{
if (!(I_FindAttr (&findstate) & FA_DIREC))
{
strcpy (skinname + stuffstart, I_FindName (&findstate));
D_AddFile (skinname);
}
} while (I_FindNext (handle, &findstate) == 0);
I_FindClose (handle);
}
}
modifiedgame = false;
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] != '-' && myargv[p][0] != '+')
D_AddFile (myargv[p]);
}
if (!(p = M_CheckParm ("-playdemo")) )
if (!(p = M_CheckParm ("-timedemo")) ) {
if ( (p = M_CheckParm ("-fastdemo")) )
fastdemo = true;
}
W_InitMultipleFiles (&wadfiles);
// [RH] Moved these up here so that we can do most of our
// startup output in a fullscreen console.
CT_Init ();
I_Init ();
I_InitGraphics ();
V_Init ();
// [RH] Initialize configurable strings.
D_InitStrings ();
// [RH] Apply any DeHackEd patch
{
int hack;
cvar_t *autopatch;
hack = M_CheckParm ("-deh");
if (hack && hack < myargc - 1)
// Use patch from command line
DoDehPatch (myargv[hack + 1], false);
else {
autopatch = cvar ("def_patch", "", CVAR_ARCHIVE);
if (FileExists (autopatch->string))
// Use patch specified by def_patch.
// (Any patches in a PWAD take precedence.)
DoDehPatch (autopatch->string, true);
else
DoDehPatch (NULL, true); // See if there's a patch in a PWAD
}
}
// [RH] User-configurable startup strings. Because BOOM does.
if (STARTUP1[0]) Printf (PRINT_HIGH, "%s\n", STARTUP1);
if (STARTUP2[0]) Printf (PRINT_HIGH, "%s\n", STARTUP2);
if (STARTUP3[0]) Printf (PRINT_HIGH, "%s\n", STARTUP3);
if (STARTUP4[0]) Printf (PRINT_HIGH, "%s\n", STARTUP4);
if (STARTUP5[0]) Printf (PRINT_HIGH, "%s\n", STARTUP5);
flags = (int)dmflagsvar->value;
dmflagsvar->u.callback = DMFlagsCallback;
if (M_CheckParm ("-nomonsters"))
flags |= DF_NO_MONSTERS;
if (M_CheckParm ("-respawn"))
flags |= DF_MONSTERS_RESPAWN;
if (M_CheckParm ("-fast"))
flags |= DF_FAST_MONSTERS;
devparm = M_CheckParm ("-devparm");
if (M_CheckParm ("-altdeath")) {
SetCVarFloat (deathmatch, 1.0f);
flags |= DF_ITEMS_RESPAWN;
} else if (M_CheckParm ("-deathmatch")) {
SetCVarFloat (deathmatch, 1.0f);
flags |= DF_WEAPONS_STAY;
}
SetCVarFloat (dmflagsvar, (float)flags);
// get skill / episode / map from parms
strcpy (startmap, gamemode == commercial ? "MAP01" : "E1M1");
autostart = false;
p = M_CheckParm ("-skill");
if (p && p < myargc-1)
{
SetCVarFloat (gameskill, (float)(myargv[p+1][0]-'1'));
autostart = true;
}
p = M_CheckParm ("-warp");
if (p && p < myargc-1)
{
int ep, map;
if (gamemode == commercial) {
ep = 1;
map = atoi (myargv[p+1]);
} else {
ep = myargv[p+1][0]-'0';
map = myargv[p+2][0]-'0';
}
strncpy (startmap, CalcMapName (ep, map), 8);
autostart = true;
}
// [RH] Hack to handle +map
p = M_CheckParm ("+map");
if (p && p < myargc-1) {
strncpy (startmap, myargv[p+1], 8);
myargv[p][0] = '-';
autostart = true;
}
if (devparm)
Printf (PRINT_HIGH, Strings[0].builtin); // D_DEVSTR
if (M_CheckParm("-cdrom"))
{
Printf (PRINT_HIGH, Strings[1].builtin); // D_CDROM
mkdir ("c:\\zdoomdat", 0);
}
// turbo option
// [RH] (Also exists as a cvar now.)
{
cvar_t *turbo = cvar ("turbo", "0", CVAR_CALLBACK);
float tval;
turbo->u.callback = TurboCallback;
if ( (p=M_CheckParm ("-turbo")) ) {
if (p < myargc - 1)
tval = (float)atof (myargv[p+1]);
else
tval = 200.0f;
Printf (PRINT_HIGH, "turbo scale: %g%%\n", tval);
} else {
tval = 100.0f;
}
SetCVarFloat (turbo, tval);
}
p = M_CheckParm ("-timer");
if (p && p < myargc-1)
{
double time = atof (myargv[p+1]);
Printf (PRINT_HIGH, "Levels will end after %g minute%s.\n", time, time > 1 ? "s" : "");
SetCVarFloat (timelimit, (float)time);
}
p = M_CheckParm ("-avg");
if (p && p < myargc-1) {
Printf (PRINT_HIGH, "Austin Virtual Gaming: Levels will end after 20 minutes\n");
SetCVarFloat (timelimit, 20);
}
// [RH] Now that all text strings are set up,
// insert them into the level and cluster data.
G_SetLevelStrings ();
// [RH] Parse through all loaded mapinfo lumps
G_ParseMapInfo ();
// [RH] Parse any SNDINFO lumps
S_ParseSndInfo();
// Check for -file in shareware
// [RH] Don't check for a bunch of lumps not found in shareware.
if (modifiedgame && gamemode == shareware)
I_Error("\nYou cannot -file with the shareware version. Register!");
#if 0
// Iff additonal PWAD files are used, print modified banner
if (modifiedgame)
{
/*m*/Printf (PRINT_HIGH,
"===========================================================================\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"
"===========================================================================\n"
);
}
// Check and print which version is executed.
switch ( gamemode )
{
case shareware:
case undetermined:
Printf (PRINT_HIGH,
"===========================================================================\n"
" Shareware!\n"
"===========================================================================\n"
);
break;
case registered:
case retail:
case commercial:
Printf (PRINT_HIGH,
"===========================================================================\n"
" Commercial product - do not distribute!\n"
" Please report software piracy to the SPA: 1-800-388-PIR8\n"
"===========================================================================\n"
);
break;
default:
// Ouch.
break;
}
#endif
Printf (PRINT_HIGH, "M_Init: Init miscellaneous info.\n");
M_Init ();
Printf (PRINT_HIGH, "R_Init: Init DOOM refresh daemon");
R_Init ();
Printf (PRINT_HIGH, "\nP_Init: Init Playloop state.");
P_Init ();
Printf (PRINT_HIGH, "\nS_Init: Setting up sound.\n");
S_Init ((int)snd_SfxVolume->value /* *8 */, snd_MusicVolume->value /* *8*/ );
Printf (PRINT_HIGH, "D_CheckNetGame: Checking network game status.\n");
D_CheckNetGame ();
Printf (PRINT_HIGH, "ST_Init: Init status bar.\n");
ST_Init ();
// start the apropriate game based on parms
p = M_CheckParm ("-record");
if (p && p < myargc-1)
{
G_RecordDemo (myargv[p+1]);
autostart = true;
}
p = M_CheckParm ("-playdemo");
if (p && p < myargc-1)
{
char blah[256];
singledemo = true; // quit after one demo
strcpy (blah, myargv[p+1]);
FixPathSeperator (blah);
ExtractFileBase (blah, file);
G_DeferedPlayDemo (file);
D_ErrorLoop (); // never returns
}
p = M_CheckParm ("-fastdemo");
if (p && p < myargc-1)
{
extern BOOL timingdemo;
singledemo = true; // quit after one demo
timingdemo = true; // show stats after quit
G_DeferedPlayDemo(myargv[p+1]);
D_ErrorLoop(); // never returns
}
p = M_CheckParm ("-timedemo");
if (p && p < myargc-1)
{
G_TimeDemo (myargv[p+1]);
D_ErrorLoop (); // never returns
}
p = M_CheckParm ("-loadgame");
if (p && p < myargc-1)
{
G_BuildSaveName (file, myargv[p+1][0] - '0');
G_LoadGame (file);
}
// [RH] Initialize items. Still only used for the give command. :-(
InitItems ();
// [RH] Lock any cvars that should be locked now that we're
// about to begin the game.
C_EnableNoSet ();
// [RH] Now that all game subsystems have been initialized,
// do all commands on the command line other than +set
C_ExecCmdLineParams (false);
if (gameaction != ga_loadgame)
{
BorderNeedRefresh = true;
if (autostart || netgame)
{
G_InitNew (startmap);
}
else
{
D_StartTitle (); // start up intro loop
}
}
if (demorecording)
G_BeginRecording ();
if (M_CheckParm ("-debugfile"))
{
char filename[20];
sprintf (filename,"debug%i.txt",consoleplayer);
Printf (PRINT_HIGH, "debug output to: %s\n",filename);
debugfile = fopen (filename,"w");
}
atexit (D_QuitNetGame); // killough
D_ErrorLoop (); // [RH] Wrap D_DoomLoop inside another function
}