mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-04-25 20:20:57 +00:00
1388 lines
31 KiB
C
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
|
|
}
|