ref: 6537d7e90c48d76eb982701de8d0af6feb147fd0
dir: /src/heretic/d_main.c/
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 1993-2008 Raven Software
// Copyright(C) 2008 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
//-----------------------------------------------------------------------------
// D_main.c
#include <stdio.h>
#include <stdlib.h>
#include "doomfeatures.h"
#include "txt_main.h"
#include "txt_io.h"
#include "net_client.h"
#include "config.h"
#include "ct_chat.h"
#include "doomdef.h"
#include "deh_main.h"
#include "d_iwad.h"
#include "i_endoom.h"
#include "i_joystick.h"
#include "i_sound.h"
#include "i_system.h"
#include "i_timer.h"
#include "i_video.h"
#include "m_argv.h"
#include "m_config.h"
#include "m_controls.h"
#include "m_misc.h"
#include "p_local.h"
#include "s_sound.h"
#include "w_main.h"
#include "v_video.h"
#define CT_KEY_GREEN 'g'
#define CT_KEY_YELLOW 'y'
#define CT_KEY_RED 'r'
#define CT_KEY_BLUE 'b'
#define STARTUP_WINDOW_X 17
#define STARTUP_WINDOW_Y 7
GameMode_t gamemode = indetermined;
char *gamedescription = "unknown";
boolean nomonsters; // checkparm of -nomonsters
boolean respawnparm; // checkparm of -respawn
boolean debugmode; // checkparm of -debug
boolean ravpic; // checkparm of -ravpic
boolean cdrom; // true if cd-rom mode active
boolean singletics; // debug flag to cancel adaptiveness
boolean noartiskip; // whether shift-enter skips an artifact
skill_t startskill;
int startepisode;
int startmap;
int UpdateState;
static int graphical_startup = 1;
static boolean using_graphical_startup;
static boolean main_loop_started = false;
boolean autostart;
extern boolean automapactive;
boolean advancedemo;
FILE *debugfile;
static int show_endoom = 1;
void D_ConnectNetGame(void);
void D_CheckNetGame(void);
void D_PageDrawer(void);
void D_AdvanceDemo(void);
boolean F_Responder(event_t * ev);
//---------------------------------------------------------------------------
//
// PROC D_ProcessEvents
//
// Send all the events of the given timestamp down the responder chain.
//
//---------------------------------------------------------------------------
void D_ProcessEvents(void)
{
event_t *ev;
while ((ev = D_PopEvent()) != NULL)
{
if (F_Responder(ev))
{
continue;
}
if (MN_Responder(ev))
{
continue;
}
G_Responder(ev);
}
}
//---------------------------------------------------------------------------
//
// PROC DrawMessage
//
//---------------------------------------------------------------------------
void DrawMessage(void)
{
player_t *player;
player = &players[consoleplayer];
if (player->messageTics <= 0 || !player->message)
{ // No message
return;
}
MN_DrTextA(player->message, 160 - MN_TextAWidth(player->message) / 2, 1);
}
//---------------------------------------------------------------------------
//
// PROC D_Display
//
// Draw current display, possibly wiping it from the previous.
//
//---------------------------------------------------------------------------
void R_ExecuteSetViewSize(void);
extern boolean finalestage;
void D_Display(void)
{
extern boolean askforquit;
// Change the view size if needed
if (setsizeneeded)
{
R_ExecuteSetViewSize();
}
//
// do buffered drawing
//
switch (gamestate)
{
case GS_LEVEL:
if (!gametic)
break;
if (automapactive)
AM_Drawer();
else
R_RenderPlayerView(&players[displayplayer]);
CT_Drawer();
UpdateState |= I_FULLVIEW;
SB_Drawer();
break;
case GS_INTERMISSION:
IN_Drawer();
break;
case GS_FINALE:
F_Drawer();
break;
case GS_DEMOSCREEN:
D_PageDrawer();
break;
}
if (testcontrols)
{
V_DrawMouseSpeedBox(testcontrols_mousespeed);
}
if (paused && !MenuActive && !askforquit)
{
if (!netgame)
{
V_DrawPatch(160, viewwindowy + 5, W_CacheLumpName(DEH_String("PAUSED"),
PU_CACHE));
}
else
{
V_DrawPatch(160, 70, W_CacheLumpName(DEH_String("PAUSED"), PU_CACHE));
}
}
// Handle player messages
DrawMessage();
// Menu drawing
MN_Drawer();
// Send out any new accumulation
NetUpdate();
// Flush buffered stuff to screen
I_FinishUpdate();
}
//
// D_GrabMouseCallback
//
// Called to determine whether to grab the mouse pointer
//
boolean D_GrabMouseCallback(void)
{
// when menu is active or game is paused, release the mouse
if (MenuActive || paused)
return false;
// only grab mouse when playing levels (but not demos)
return (gamestate == GS_LEVEL) && !demoplayback && !advancedemo;
}
//---------------------------------------------------------------------------
//
// PROC D_DoomLoop
//
//---------------------------------------------------------------------------
void D_DoomLoop(void)
{
if (M_CheckParm("-debugfile"))
{
char filename[20];
M_snprintf(filename, sizeof(filename), "debug%i.txt", consoleplayer);
debugfile = fopen(filename, "w");
}
I_GraphicsCheckCommandLine();
I_SetGrabMouseCallback(D_GrabMouseCallback);
I_InitGraphics();
main_loop_started = true;
while (1)
{
// Frame syncronous IO operations
I_StartFrame();
// Process one or more tics
// Will run at least one tic
TryRunTics();
// Move positional sounds
S_UpdateSounds(players[consoleplayer].mo);
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_DrawRawScreen(W_CacheLumpName(pagename, PU_CACHE));
if (demosequence == 1)
{
V_DrawPatch(4, 160, W_CacheLumpName(DEH_String("ADVISOR"), PU_CACHE));
}
UpdateState |= I_FULLSCRN;
}
/*
=================
=
= D_AdvanceDemo
=
= Called after each demo or intro demosequence finishes
=================
*/
void D_AdvanceDemo(void)
{
advancedemo = true;
}
void D_DoAdvanceDemo(void)
{
players[consoleplayer].playerstate = PST_LIVE; // don't reborn
advancedemo = false;
usergame = false; // can't save / end game here
paused = false;
gameaction = ga_nothing;
demosequence = (demosequence + 1) % 7;
switch (demosequence)
{
case 0:
pagetic = 210;
gamestate = GS_DEMOSCREEN;
pagename = DEH_String("TITLE");
S_StartSong(mus_titl, false);
break;
case 1:
pagetic = 140;
gamestate = GS_DEMOSCREEN;
pagename = DEH_String("TITLE");
break;
case 2:
BorderNeedRefresh = true;
UpdateState |= I_FULLSCRN;
G_DeferedPlayDemo(DEH_String("demo1"));
break;
case 3:
pagetic = 200;
gamestate = GS_DEMOSCREEN;
pagename = DEH_String("CREDIT");
break;
case 4:
BorderNeedRefresh = true;
UpdateState |= I_FULLSCRN;
G_DeferedPlayDemo(DEH_String("demo2"));
break;
case 5:
pagetic = 200;
gamestate = GS_DEMOSCREEN;
if (gamemode == shareware)
{
pagename = DEH_String("ORDER");
}
else
{
pagename = DEH_String("CREDIT");
}
break;
case 6:
BorderNeedRefresh = true;
UpdateState |= I_FULLSCRN;
G_DeferedPlayDemo(DEH_String("demo3"));
break;
}
}
/*
=================
=
= D_StartTitle
=
=================
*/
void D_StartTitle(void)
{
gameaction = ga_nothing;
demosequence = -1;
D_AdvanceDemo();
}
/*
==============
=
= D_CheckRecordFrom
=
= -recordfrom <savegame num> <demoname>
==============
*/
void D_CheckRecordFrom(void)
{
int p;
char *filename;
//!
// @vanilla
// @category demo
// @arg <savenum> <demofile>
//
// Record a demo, loading from the given filename. Equivalent
// to -loadgame <savenum> -record <demofile>.
p = M_CheckParmWithArgs("-recordfrom", 2);
if (!p)
return;
filename = SV_Filename(myargv[p + 1][0] - '0');
G_LoadGame(filename);
G_DoLoadGame(); // load the gameskill etc info from savegame
G_RecordDemo(gameskill, 1, gameepisode, gamemap, myargv[p + 2]);
D_DoomLoop(); // never returns
free(filename);
}
/*
===============
=
= D_AddFile
=
===============
*/
// MAPDIR should be defined as the directory that holds development maps
// for the -wart # # command
#define MAPDIR "\\data\\"
#define SHAREWAREWADNAME "heretic1.wad"
char *iwadfile;
char *basedefault = "heretic.cfg";
void wadprintf(void)
{
if (debugmode)
{
return;
}
// haleyjd FIXME: convert to textscreen code?
#ifdef __WATCOMC__
_settextposition(23, 2);
_setbkcolor(1);
_settextcolor(0);
_outtext(exrnwads);
_settextposition(24, 2);
_outtext(exrnwads2);
#endif
}
boolean D_AddFile(char *file)
{
wad_file_t *handle;
printf(" adding %s\n", file);
handle = W_AddFile(file);
return handle != NULL;
}
//==========================================================
//
// Startup Thermo code
//
//==========================================================
#define MSG_Y 9
#define THERM_X 14
#define THERM_Y 14
int thermMax;
int thermCurrent;
char smsg[80]; // status bar line
//
// Heretic startup screen shit
//
static int startup_line = STARTUP_WINDOW_Y;
void hprintf(char *string)
{
if (using_graphical_startup)
{
TXT_BGColor(TXT_COLOR_CYAN, 0);
TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
TXT_GotoXY(STARTUP_WINDOW_X, startup_line);
++startup_line;
TXT_Puts(string);
TXT_UpdateScreen();
}
// haleyjd: shouldn't be WATCOMC-only
if (debugmode)
puts(string);
}
void drawstatus(void)
{
int i;
TXT_GotoXY(1, 24);
TXT_BGColor(TXT_COLOR_BLUE, 0);
TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
for (i=0; smsg[i] != '\0'; ++i)
{
TXT_PutChar(smsg[i]);
}
}
void status(char *string)
{
if (using_graphical_startup)
{
M_StringConcat(smsg, string, sizeof(smsg));
drawstatus();
}
}
void DrawThermo(void)
{
static int last_progress = -1;
int progress;
int i;
if (!using_graphical_startup)
{
return;
}
#if 0
progress = (98 * thermCurrent) / thermMax;
screen = (char *) 0xb8000 + (THERM_Y * 160 + THERM_X * 2);
for (i = 0; i < progress / 2; i++)
{
switch (i)
{
case 4:
case 9:
case 14:
case 19:
case 29:
case 34:
case 39:
case 44:
*screen++ = 0xb3;
*screen++ = (THERMCOLOR << 4) + 15;
break;
case 24:
*screen++ = 0xba;
*screen++ = (THERMCOLOR << 4) + 15;
break;
default:
*screen++ = 0xdb;
*screen++ = 0x40 + THERMCOLOR;
break;
}
}
if (progress & 1)
{
*screen++ = 0xdd;
*screen++ = 0x40 + THERMCOLOR;
}
#else
// No progress? Don't update the screen.
progress = (50 * thermCurrent) / thermMax + 2;
if (last_progress == progress)
{
return;
}
last_progress = progress;
TXT_GotoXY(THERM_X, THERM_Y);
TXT_FGColor(TXT_COLOR_BRIGHT_GREEN);
TXT_BGColor(TXT_COLOR_GREEN, 0);
for (i = 0; i < progress; i++)
{
TXT_PutChar(0xdb);
}
TXT_UpdateScreen();
#endif
}
void initStartup(void)
{
byte *textScreen;
byte *loading;
if (!graphical_startup || debugmode || testcontrols)
{
using_graphical_startup = false;
return;
}
if (!TXT_Init())
{
using_graphical_startup = false;
return;
}
I_InitWindowTitle();
I_InitWindowIcon();
// Blit main screen
textScreen = TXT_GetScreenData();
loading = W_CacheLumpName(DEH_String("LOADING"), PU_CACHE);
memcpy(textScreen, loading, 4000);
// Print version string
TXT_BGColor(TXT_COLOR_RED, 0);
TXT_FGColor(TXT_COLOR_YELLOW);
TXT_GotoXY(46, 2);
TXT_Puts(HERETIC_VERSION_TEXT);
TXT_UpdateScreen();
using_graphical_startup = true;
}
static void finishStartup(void)
{
if (using_graphical_startup)
{
TXT_Shutdown();
}
}
char tmsg[300];
void tprintf(char *msg, int initflag)
{
// haleyjd FIXME: convert to textscreen code?
#ifdef __WATCOMC__
char temp[80];
int start;
int add;
int i;
if (initflag)
tmsg[0] = 0;
M_StringConcat(tmsg, msg, sizeof(tmsg));
blitStartup();
DrawThermo();
_setbkcolor(4);
_settextcolor(15);
for (add = start = i = 0; i <= strlen(tmsg); i++)
if ((tmsg[i] == '\n') || (!tmsg[i]))
{
memset(temp, 0, 80);
M_StringCopy(temp, tmsg + start, sizeof(temp));
if (i - start < sizeof(temp))
{
temp[i - start] = '\0';
}
_settextposition(MSG_Y + add, 40 - strlen(temp) / 2);
_outtext(temp);
start = i + 1;
add++;
}
_settextposition(25, 1);
drawstatus();
#else
printf("%s", msg);
#endif
}
// haleyjd: moved up, removed WATCOMC code
void CleanExit(void)
{
DEH_printf("Exited from HERETIC.\n");
exit(1);
}
void CheckAbortStartup(void)
{
// haleyjd: removed WATCOMC
// haleyjd FIXME: this should actually work in text mode too, but how to
// get input before SDL video init?
if(using_graphical_startup)
{
if(TXT_GetChar() == 27)
CleanExit();
}
}
void IncThermo(void)
{
thermCurrent++;
DrawThermo();
CheckAbortStartup();
}
void InitThermo(int max)
{
thermMax = max;
thermCurrent = 0;
}
//
// Add configuration file variable bindings.
//
void D_BindVariables(void)
{
extern int screenblocks;
extern int snd_Channels;
int i;
M_ApplyPlatformDefaults();
I_BindVideoVariables();
I_BindJoystickVariables();
I_BindSoundVariables();
M_BindBaseControls();
M_BindHereticControls();
M_BindWeaponControls();
M_BindChatControls(MAXPLAYERS);
key_multi_msgplayer[0] = CT_KEY_GREEN;
key_multi_msgplayer[1] = CT_KEY_YELLOW;
key_multi_msgplayer[2] = CT_KEY_RED;
key_multi_msgplayer[3] = CT_KEY_BLUE;
M_BindMenuControls();
M_BindMapControls();
M_BindVariable("mouse_sensitivity", &mouseSensitivity);
M_BindVariable("sfx_volume", &snd_MaxVolume);
M_BindVariable("music_volume", &snd_MusicVolume);
M_BindVariable("screenblocks", &screenblocks);
M_BindVariable("snd_channels", &snd_Channels);
M_BindVariable("show_endoom", &show_endoom);
M_BindVariable("graphical_startup", &graphical_startup);
for (i=0; i<10; ++i)
{
char buf[12];
M_snprintf(buf, sizeof(buf), "chatmacro%i", i);
M_BindVariable(buf, &chat_macros[i]);
}
}
//
// Called at exit to display the ENDOOM screen (ENDTEXT in Heretic)
//
static void D_Endoom(void)
{
byte *endoom_data;
// Disable ENDOOM?
if (!show_endoom || testcontrols || !main_loop_started)
{
return;
}
endoom_data = W_CacheLumpName(DEH_String("ENDTEXT"), PU_STATIC);
I_Endoom(endoom_data);
}
//---------------------------------------------------------------------------
//
// PROC D_DoomMain
//
//---------------------------------------------------------------------------
void D_DoomMain(void)
{
GameMission_t gamemission;
int p;
char file[256];
char demolumpname[9];
I_PrintBanner(PACKAGE_STRING);
I_AtExit(D_Endoom, false);
//!
// @vanilla
//
// Disable monsters.
//
nomonsters = M_ParmExists("-nomonsters");
//!
// @vanilla
//
// Monsters respawn after being killed.
//
respawnparm = M_ParmExists("-respawn");
//!
// @vanilla
//
// Take screenshots when F1 is pressed.
//
ravpic = M_ParmExists("-ravpic");
//!
// @vanilla
//
// Allow artifacts to be used when the run key is held down.
//
noartiskip = M_ParmExists("-noartiskip");
debugmode = M_ParmExists("-debug");
startskill = sk_medium;
startepisode = 1;
startmap = 1;
autostart = false;
//
// get skill / episode / map from parms
//
//!
// @vanilla
// @category net
//
// Start a deathmatch game.
//
if (M_ParmExists("-deathmatch"))
{
deathmatch = true;
}
//!
// @arg <skill>
// @vanilla
//
// Set the game skill, 1-5 (1: easiest, 5: hardest). A skill of
// 0 disables all monsters.
//
p = M_CheckParmWithArgs("-skill", 1);
if (p)
{
startskill = myargv[p + 1][0] - '1';
autostart = true;
}
//!
// @arg <n>
// @vanilla
//
// Start playing on episode n (1-4)
//
p = M_CheckParmWithArgs("-episode", 1);
if (p)
{
startepisode = myargv[p + 1][0] - '0';
startmap = 1;
autostart = true;
}
//!
// @arg <x> <y>
// @vanilla
//
// Start a game immediately, warping to level ExMy.
//
p = M_CheckParmWithArgs("-warp", 2);
if (p && p < myargc - 2)
{
startepisode = myargv[p + 1][0] - '0';
startmap = myargv[p + 2][0] - '0';
autostart = true;
}
//
// init subsystems
//
DEH_printf("V_Init: allocate screens.\n");
V_Init();
// Check for -CDROM
cdrom = false;
#ifdef _WIN32
//!
// @platform windows
// @vanilla
//
// Save configuration data and savegames in c:\heretic.cd,
// allowing play from CD.
//
if (M_CheckParm("-cdrom"))
{
cdrom = true;
}
#endif
if (cdrom)
{
M_SetConfigDir(DEH_String("c:\\heretic.cd"));
}
else
{
M_SetConfigDir(NULL);
}
// Load defaults before initing other systems
DEH_printf("M_LoadDefaults: Load system defaults.\n");
D_BindVariables();
M_SetConfigFilenames("heretic.cfg", PROGRAM_PREFIX "heretic.cfg");
M_LoadDefaults();
I_AtExit(M_SaveDefaults, false);
DEH_printf("Z_Init: Init zone memory allocation daemon.\n");
Z_Init();
#ifdef FEATURE_DEHACKED
printf("DEH_Init: Init Dehacked support.\n");
DEH_Init();
#endif
DEH_printf("W_Init: Init WADfiles.\n");
iwadfile = D_FindIWAD(IWAD_MASK_HERETIC, &gamemission);
if (iwadfile == NULL)
{
I_Error("Game mode indeterminate. No IWAD was found. Try specifying\n"
"one with the '-iwad' command line parameter.");
}
D_AddFile(iwadfile);
W_ParseCommandLine();
//!
// @arg <demo>
// @category demo
// @vanilla
//
// Play back the demo named demo.lmp.
//
p = M_CheckParmWithArgs("-playdemo", 1);
if (!p)
{
//!
// @arg <demo>
// @category demo
// @vanilla
//
// Play back the demo named demo.lmp, determining the framerate
// of the screen.
//
p = M_CheckParmWithArgs("-timedemo", 1);
}
if (p)
{
// In Vanilla, the filename must be specified without .lmp,
// but make that optional.
if (M_StringEndsWith(myargv[p + 1], ".lmp"))
{
M_StringCopy(file, myargv[p + 1], sizeof(file));
}
else
{
DEH_snprintf(file, sizeof(file), "%s.lmp", myargv[p + 1]);
}
if (D_AddFile(file))
{
M_StringCopy(demolumpname, lumpinfo[numlumps - 1].name,
sizeof(demolumpname));
}
else
{
// The file failed to load, but copy the original arg as a
// demo name to make tricks like -playdemo demo1 possible.
M_StringCopy(demolumpname, myargv[p + 1], sizeof(demolumpname));
}
printf("Playing demo %s.\n", file);
}
if (W_CheckNumForName(DEH_String("E2M1")) == -1)
{
gamemode = shareware;
gamedescription = "Heretic (shareware)";
}
else if (W_CheckNumForName("EXTENDED") != -1)
{
// Presence of the EXTENDED lump indicates the retail version
gamemode = retail;
gamedescription = "Heretic: Shadow of the Serpent Riders";
}
else
{
gamemode = registered;
gamedescription = "Heretic (registered)";
}
I_SetWindowTitle(gamedescription);
savegamedir = M_GetSaveGameDir("heretic.wad");
I_PrintStartupBanner(gamedescription);
if (M_ParmExists("-testcontrols"))
{
startepisode = 1;
startmap = 1;
autostart = true;
testcontrols = true;
}
I_InitTimer();
I_InitSound(false);
I_InitMusic();
#ifdef FEATURE_MULTIPLAYER
tprintf("NET_Init: Init network subsystem.\n", 1);
NET_Init ();
#endif
D_ConnectNetGame();
// haleyjd: removed WATCOMC
initStartup();
//
// Build status bar line!
//
smsg[0] = 0;
if (deathmatch)
status(DEH_String("DeathMatch..."));
if (nomonsters)
status(DEH_String("No Monsters..."));
if (respawnparm)
status(DEH_String("Respawning..."));
if (autostart)
{
char temp[64];
DEH_snprintf(temp, sizeof(temp),
"Warp to Episode %d, Map %d, Skill %d ",
startepisode, startmap, startskill + 1);
status(temp);
}
wadprintf(); // print the added wadfiles
tprintf(DEH_String("MN_Init: Init menu system.\n"), 1);
MN_Init();
CT_Init();
tprintf(DEH_String("R_Init: Init Heretic refresh daemon."), 1);
hprintf(DEH_String("Loading graphics"));
R_Init();
tprintf("\n", 0);
tprintf(DEH_String("P_Init: Init Playloop state.\n"), 1);
hprintf(DEH_String("Init game engine."));
P_Init();
IncThermo();
tprintf(DEH_String("I_Init: Setting up machine state.\n"), 1);
I_CheckIsScreensaver();
I_InitJoystick();
IncThermo();
tprintf(DEH_String("S_Init: Setting up sound.\n"), 1);
S_Init();
//IO_StartupTimer();
S_Start();
tprintf(DEH_String("D_CheckNetGame: Checking network game status.\n"), 1);
hprintf(DEH_String("Checking network game status."));
D_CheckNetGame();
IncThermo();
// haleyjd: removed WATCOMC
tprintf(DEH_String("SB_Init: Loading patches.\n"), 1);
SB_Init();
IncThermo();
//
// start the apropriate game based on parms
//
D_CheckRecordFrom();
//!
// @arg <x>
// @category demo
// @vanilla
//
// Record a demo named x.lmp.
//
p = M_CheckParmWithArgs("-record", 1);
if (p)
{
G_RecordDemo(startskill, 1, startepisode, startmap, myargv[p + 1]);
D_DoomLoop(); // Never returns
}
p = M_CheckParmWithArgs("-playdemo", 1);
if (p)
{
singledemo = true; // Quit after one demo
G_DeferedPlayDemo(demolumpname);
D_DoomLoop(); // Never returns
}
p = M_CheckParmWithArgs("-timedemo", 1);
if (p)
{
G_TimeDemo(demolumpname);
D_DoomLoop(); // Never returns
}
//!
// @arg <s>
// @vanilla
//
// Load the game in savegame slot s.
//
p = M_CheckParmWithArgs("-loadgame", 1);
if (p && p < myargc - 1)
{
char *filename;
filename = SV_Filename(myargv[p + 1][0] - '0');
G_LoadGame(filename);
free(filename);
}
// Check valid episode and map
if (autostart || netgame)
{
if (!D_ValidEpisodeMap(heretic, gamemode, startepisode, startmap))
{
startepisode = 1;
startmap = 1;
}
}
if (gameaction != ga_loadgame)
{
UpdateState |= I_FULLSCRN;
BorderNeedRefresh = true;
if (autostart || netgame)
{
G_InitNew(startskill, startepisode, startmap);
}
else
{
D_StartTitle();
}
}
finishStartup();
D_DoomLoop(); // Never returns
}