mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2025-02-21 10:51:25 +00:00
returning from one function to another function when the function that was called was used as part of an expression. - Fixed: Using Thing_Hate with arg0 (hater) set to 0 from an open script could crash. - Fixed: Some items along ledges in Hexen's MAP32 (Orchard of Lamentations) appeared at the bottom of the ledge (and consequently inside it) instead of on top of it because the items were placed directly on the lines. AActor::LinkToWorldForMapThing() needs to use the original R_PointOnLineSide() code to handle situations like this. Previously, it just used the original code for straight horizontal/vertical lines and used the new code for diagonal lines. - Fixed: FWadCollection::MergeLumps() used in incorrect realloc. - Fixed: FPlayList::NextLine() did not properly handle blank lines in the playlist. - Changed: Decals now use lightweight thinkers instead of actors. (76 bytes versus 396, so you save 320k if you have 1024 decals present.) - Fixed: Wads added with pullin were loaded immediately after the IWAD. Exec files are now processed immediately before -file but after autoloading wads in D_DoomMain(). - Fixed: sdl/i_system.h unconditionally defined SHARE_DIR, preventing redefinition from the command line. - Fixed: The standard way to include SDL.h is <SDL.h>, not <SDL/SDL.h>. - Fixed: Returned FActiveInterpolation::HashKey()'s return type to size_t, avoiding a pointer truncation warning. SVN r30 (trunk)
2358 lines
55 KiB
C++
2358 lines
55 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.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// HEADER FILES ------------------------------------------------------------
|
|
|
|
#ifdef _WIN32
|
|
#include <direct.h>
|
|
#define mkdir(a,b) _mkdir (a)
|
|
#else
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#ifdef unix
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <time.h>
|
|
#include <math.h>
|
|
#include <assert.h>
|
|
|
|
#include "doomerrors.h"
|
|
|
|
#include "d_gui.h"
|
|
#include "m_alloc.h"
|
|
#include "m_random.h"
|
|
#include "doomdef.h"
|
|
#include "doomstat.h"
|
|
#include "gstrings.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_console.h"
|
|
#include "c_dispatch.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_dehacked.h"
|
|
#include "cmdlib.h"
|
|
#include "s_sound.h"
|
|
#include "m_swap.h"
|
|
#include "v_text.h"
|
|
#include "gi.h"
|
|
#include "b_bot.h" //Added by MC:
|
|
#include "stats.h"
|
|
#include "a_doomglobal.h"
|
|
#include "gameconfigfile.h"
|
|
#include "sbar.h"
|
|
#include "decallib.h"
|
|
#include "r_polymost.h"
|
|
|
|
#include "v_text.h"
|
|
|
|
// MACROS ------------------------------------------------------------------
|
|
|
|
// TYPES -------------------------------------------------------------------
|
|
|
|
struct GameAtExit
|
|
{
|
|
GameAtExit *Next;
|
|
char Command[1];
|
|
};
|
|
|
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
|
|
|
extern void M_RestoreMode ();
|
|
extern void M_SetDefaultMode ();
|
|
extern void R_ExecuteSetViewSize ();
|
|
extern void G_NewInit ();
|
|
|
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
|
|
|
void D_CheckNetGame ();
|
|
void D_ProcessEvents ();
|
|
void G_BuildTiccmd (ticcmd_t* cmd);
|
|
void D_DoAdvanceDemo ();
|
|
void D_AddFile (const char *file);
|
|
void D_AddWildFile (const char *pattern);
|
|
|
|
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
|
|
|
void D_DoomLoop ();
|
|
static const char *BaseFileSearch (const char *file, const char *ext);
|
|
static void STACK_ARGS DoConsoleAtExit ();
|
|
|
|
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
|
|
|
EXTERN_CVAR (Float, turbo)
|
|
EXTERN_CVAR (Int, crosshair)
|
|
EXTERN_CVAR (Bool, freelook)
|
|
EXTERN_CVAR (Float, m_pitch)
|
|
EXTERN_CVAR (Float, m_yaw)
|
|
EXTERN_CVAR (Bool, invertmouse)
|
|
EXTERN_CVAR (Bool, lookstrafe)
|
|
|
|
extern gameinfo_t SharewareGameInfo;
|
|
extern gameinfo_t RegisteredGameInfo;
|
|
extern gameinfo_t RetailGameInfo;
|
|
extern gameinfo_t CommercialGameInfo;
|
|
extern gameinfo_t HereticGameInfo;
|
|
extern gameinfo_t HereticSWGameInfo;
|
|
extern gameinfo_t HexenGameInfo;
|
|
extern gameinfo_t HexenDKGameInfo;
|
|
extern gameinfo_t StrifeGameInfo;
|
|
extern gameinfo_t StrifeTeaserGameInfo;
|
|
extern gameinfo_t StrifeTeaser2GameInfo;
|
|
|
|
extern int testingmode;
|
|
extern BOOL setmodeneeded;
|
|
extern BOOL netdemo;
|
|
extern int NewWidth, NewHeight, NewBits, DisplayBits;
|
|
EXTERN_CVAR (Bool, st_scale)
|
|
extern BOOL gameisdead;
|
|
extern BOOL demorecording;
|
|
extern bool M_DemoNoPlay; // [RH] if true, then skip any demos in the loop
|
|
|
|
extern cycle_t WallCycles, PlaneCycles, MaskedCycles, WallScanCycles;
|
|
|
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
|
|
CVAR (Int, fraglimit, 0, CVAR_SERVERINFO);
|
|
CVAR (Float, timelimit, 0.f, CVAR_SERVERINFO);
|
|
CVAR (Bool, queryiwad, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
|
CVAR (Int, wipetype, 1, CVAR_ARCHIVE);
|
|
|
|
bool DrawFSHUD; // [RH] Draw fullscreen HUD?
|
|
wadlist_t *wadfiles; // [RH] remove limit on # of loaded wads
|
|
BOOL devparm; // started game with -devparm
|
|
char *D_DrawIcon; // [RH] Patch name of icon to draw on next refresh
|
|
int NoWipe; // [RH] Allow wipe? (Needs to be set each time)
|
|
BOOL singletics = false; // debug flag to cancel adaptiveness
|
|
char startmap[8];
|
|
BOOL autostart;
|
|
BOOL advancedemo;
|
|
FILE *debugfile;
|
|
event_t events[MAXEVENTS];
|
|
int eventhead;
|
|
int eventtail;
|
|
gamestate_t wipegamestate = GS_DEMOSCREEN; // can be -1 to force a wipe
|
|
bool PageBlank;
|
|
FTexture *Page;
|
|
FTexture *Advisory;
|
|
|
|
cycle_t FrameCycles;
|
|
|
|
const char *IWADTypeNames[NUM_IWAD_TYPES] =
|
|
{
|
|
"DOOM 2: TNT - Evilution",
|
|
"DOOM 2: Plutonia Experiment",
|
|
"Hexen: Beyond Heretic",
|
|
"Hexen: Deathkings of the Dark Citadel",
|
|
"DOOM 2: Hell on Earth",
|
|
"Heretic Shareware",
|
|
"Heretic: Shadow of the Serpent Riders",
|
|
"Heretic",
|
|
"DOOM Shareware",
|
|
"The Ultimate DOOM",
|
|
"DOOM Registered",
|
|
"Strife: Quest for the Sigil",
|
|
"Strife: Teaser (Old Version)",
|
|
"Strife: Teaser (New Version)"
|
|
};
|
|
|
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
|
|
|
static wadlist_t **wadtail = &wadfiles;
|
|
static int demosequence;
|
|
static int pagetic;
|
|
static const char *IWADNames[] =
|
|
{
|
|
NULL,
|
|
"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",
|
|
"heretic.wad",
|
|
"heretic1.wad",
|
|
"hexen.wad",
|
|
"hexdd.wad",
|
|
"strife1.wad",
|
|
"strife0.wad",
|
|
NULL
|
|
};
|
|
static GameAtExit *ExitCmdList;
|
|
|
|
// CODE --------------------------------------------------------------------
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_ProcessEvents
|
|
//
|
|
// Send all the events of the given timestamp down the responder chain.
|
|
// Events are asynchronous inputs generally generated by the game user.
|
|
// Events can be discarded if no responder claims them
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_ProcessEvents (void)
|
|
{
|
|
event_t *ev;
|
|
|
|
// [RH] If testing mode, do not accept input until test is over
|
|
if (testingmode)
|
|
{
|
|
if (testingmode == 1)
|
|
{
|
|
M_SetDefaultMode ();
|
|
}
|
|
else if (testingmode <= I_GetTime(false))
|
|
{
|
|
M_RestoreMode ();
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (; eventtail != eventhead ; eventtail = (eventtail+1)&(MAXEVENTS-1))
|
|
{
|
|
ev = &events[eventtail];
|
|
if (C_Responder (ev))
|
|
continue; // console ate the event
|
|
if (M_Responder (ev))
|
|
continue; // menu ate the event
|
|
if (testpolymost)
|
|
Polymost_Responder (ev);
|
|
G_Responder (ev);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_PostEvent
|
|
//
|
|
// Called by the I/O functions when input is detected.
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_PostEvent (const event_t *ev)
|
|
{
|
|
events[eventhead] = *ev;
|
|
if (ev->type == EV_Mouse && !testpolymost && !paused && menuactive == MENU_Off &&
|
|
ConsoleState != c_down && ConsoleState != c_falling)
|
|
{
|
|
if (Button_Mlook.bDown || freelook)
|
|
{
|
|
int look = int(ev->y * m_pitch * mouse_sensitivity * 16.0);
|
|
if (invertmouse)
|
|
look = -look;
|
|
G_AddViewPitch (look);
|
|
events[eventhead].y = 0;
|
|
}
|
|
if (!Button_Strafe.bDown && !lookstrafe)
|
|
{
|
|
G_AddViewAngle (int(ev->x * m_yaw * mouse_sensitivity * 8.0));
|
|
events[eventhead].x = 0;
|
|
}
|
|
if ((events[eventhead].x | events[eventhead].y) == 0)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
eventhead = (eventhead+1)&(MAXEVENTS-1);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CVAR dmflags
|
|
//
|
|
//==========================================================================
|
|
|
|
CUSTOM_CVAR (Int, dmflags, 0, CVAR_SERVERINFO)
|
|
{
|
|
// In case DF_NO_FREELOOK was changed, reinitialize the sky
|
|
// map. (If no freelook, then no need to stretch the sky.)
|
|
if (sky1texture != 0)
|
|
R_InitSkyMap ();
|
|
|
|
if (self & DF_NO_FREELOOK)
|
|
{
|
|
Net_WriteByte (DEM_CENTERVIEW);
|
|
}
|
|
// If nofov is set, force everybody to the arbitrator's FOV.
|
|
if ((self & DF_NO_FOV) && consoleplayer == Net_Arbitrator)
|
|
{
|
|
BYTE fov;
|
|
|
|
Net_WriteByte (DEM_FOV);
|
|
|
|
// If the game is started with DF_NO_FOV set, the arbitrator's
|
|
// DesiredFOV will not be set when this callback is run, so
|
|
// be sure not to transmit a 0 FOV.
|
|
fov = (BYTE)players[consoleplayer].DesiredFOV;
|
|
if (fov == 0)
|
|
{
|
|
fov = 90;
|
|
}
|
|
Net_WriteByte (fov);
|
|
}
|
|
}
|
|
|
|
CVAR (Flag, sv_nohealth, dmflags, DF_NO_HEALTH);
|
|
CVAR (Flag, sv_noitems, dmflags, DF_NO_ITEMS);
|
|
CVAR (Flag, sv_weaponstay, dmflags, DF_WEAPONS_STAY);
|
|
CVAR (Flag, sv_falldamage, dmflags, DF_FORCE_FALLINGHX);
|
|
CVAR (Flag, sv_oldfalldamage, dmflags, DF_FORCE_FALLINGZD);
|
|
CVAR (Flag, sv_samelevel, dmflags, DF_SAME_LEVEL);
|
|
CVAR (Flag, sv_spawnfarthest, dmflags, DF_SPAWN_FARTHEST);
|
|
CVAR (Flag, sv_forcerespawn, dmflags, DF_FORCE_RESPAWN);
|
|
CVAR (Flag, sv_noarmor, dmflags, DF_NO_ARMOR);
|
|
CVAR (Flag, sv_noexit, dmflags, DF_NO_EXIT);
|
|
CVAR (Flag, sv_infiniteammo, dmflags, DF_INFINITE_AMMO);
|
|
CVAR (Flag, sv_nomonsters, dmflags, DF_NO_MONSTERS);
|
|
CVAR (Flag, sv_monsterrespawn, dmflags, DF_MONSTERS_RESPAWN);
|
|
CVAR (Flag, sv_itemrespawn, dmflags, DF_ITEMS_RESPAWN);
|
|
CVAR (Flag, sv_fastmonsters, dmflags, DF_FAST_MONSTERS);
|
|
CVAR (Flag, sv_nojump, dmflags, DF_NO_JUMP);
|
|
CVAR (Flag, sv_nofreelook, dmflags, DF_NO_FREELOOK);
|
|
CVAR (Flag, sv_respawnsuper, dmflags, DF_RESPAWN_SUPER);
|
|
CVAR (Flag, sv_nofov, dmflags, DF_NO_FOV);
|
|
CVAR (Flag, sv_noweaponspawn, dmflags, DF_NO_COOP_WEAPON_SPAWN);
|
|
|
|
//==========================================================================
|
|
//
|
|
// CVAR dmflags2
|
|
//
|
|
// [RH] From Skull Tag. Some of these were already done as separate cvars
|
|
// (such as bfgaiming), but I collected them here like Skull Tag does.
|
|
//
|
|
//==========================================================================
|
|
|
|
CVAR (Int, dmflags2, 0, CVAR_SERVERINFO);
|
|
CVAR (Flag, sv_weapondrop, dmflags2, DF2_YES_WEAPONDROP);
|
|
CVAR (Flag, sv_nobfgaim, dmflags2, DF2_NO_FREEAIMBFG);
|
|
CVAR (Flag, sv_respawnprotect, dmflags2, DF2_YES_INVUL);
|
|
CVAR (Flag, sv_barrelrespawn, dmflags2, DF2_BARRELS_RESPAWN);
|
|
|
|
//==========================================================================
|
|
//
|
|
// CVAR compatflags
|
|
//
|
|
//==========================================================================
|
|
|
|
CVAR (Int, compatflags, 0, CVAR_ARCHIVE|CVAR_SERVERINFO);
|
|
CVAR (Flag, compat_shortTex, compatflags, COMPATF_SHORTTEX);
|
|
CVAR (Flag, compat_stairs, compatflags, COMPATF_STAIRINDEX);
|
|
CVAR (Flag, compat_limitpain, compatflags, COMPATF_LIMITPAIN);
|
|
CVAR (Flag, compat_silentpickup,compatflags, COMPATF_SILENTPICKUP);
|
|
CVAR (Flag, compat_nopassover, compatflags, COMPATF_NO_PASSMOBJ);
|
|
CVAR (Flag, compat_soundslots, compatflags, COMPATF_MAGICSILENCE);
|
|
CVAR (Flag, compat_wallrun, compatflags, COMPATF_WALLRUN);
|
|
CVAR (Flag, compat_notossdrops, compatflags, COMPATF_NOTOSSDROPS);
|
|
CVAR (Flag, compat_useblocking, compatflags, COMPATF_USEBLOCKING);
|
|
CVAR (Flag, compat_nodoorlight, compatflags, COMPATF_NODOORLIGHT);
|
|
CVAR (Flag, compat_ravenscroll, compatflags, COMPATF_RAVENSCROLL);
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_Display
|
|
//
|
|
// Draw current display, possibly wiping it from the previous
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_Display (bool screenshot)
|
|
{
|
|
bool wipe;
|
|
|
|
if (nodrawers)
|
|
return; // for comparative timing / profiling
|
|
|
|
cycle_t cycles = 0;
|
|
clock (cycles);
|
|
|
|
if (players[consoleplayer].camera == NULL)
|
|
{
|
|
players[consoleplayer].camera = players[consoleplayer].mo;
|
|
}
|
|
|
|
if (viewactive)
|
|
{
|
|
R_SetFOV (players[consoleplayer].camera && players[consoleplayer].camera->player ?
|
|
players[consoleplayer].camera->player->FOV : 90.f);
|
|
}
|
|
|
|
// [RH] change the screen mode if needed
|
|
if (setmodeneeded)
|
|
{
|
|
// Change screen mode.
|
|
if (V_SetResolution (NewWidth, NewHeight, NewBits))
|
|
{
|
|
// Recalculate various view parameters.
|
|
setsizeneeded = true;
|
|
// Let the status bar know the screen size changed
|
|
if (StatusBar != NULL)
|
|
{
|
|
StatusBar->ScreenSizeChanged ();
|
|
}
|
|
// Refresh the console.
|
|
C_NewModeAdjust ();
|
|
// Reload crosshair if transitioned to a different size
|
|
crosshair.Callback ();
|
|
}
|
|
}
|
|
|
|
RenderTarget = screen;
|
|
|
|
// change the view size if needed
|
|
if (setsizeneeded && StatusBar != NULL)
|
|
{
|
|
R_ExecuteSetViewSize ();
|
|
}
|
|
setmodeneeded = false;
|
|
|
|
if (screen->Lock (screenshot))
|
|
{
|
|
SB_state = screen->GetPageCount ();
|
|
BorderNeedRefresh = screen->GetPageCount ();
|
|
}
|
|
|
|
// [RH] Allow temporarily disabling wipes
|
|
if (NoWipe)
|
|
{
|
|
BorderNeedRefresh = screen->GetPageCount ();
|
|
NoWipe--;
|
|
wipe = false;
|
|
wipegamestate = gamestate;
|
|
}
|
|
else if (gamestate != wipegamestate && gamestate != GS_FULLCONSOLE && gamestate != GS_TITLELEVEL)
|
|
{ // save the current screen if about to wipe
|
|
BorderNeedRefresh = screen->GetPageCount ();
|
|
wipe = true;
|
|
if (wipegamestate != GS_FORCEWIPEFADE)
|
|
{
|
|
wipe_StartScreen (wipetype);
|
|
}
|
|
else
|
|
{
|
|
wipe_StartScreen (wipe_Fade);
|
|
}
|
|
wipegamestate = gamestate;
|
|
}
|
|
else
|
|
{
|
|
wipe = false;
|
|
}
|
|
|
|
if (testpolymost)
|
|
{
|
|
drawpolymosttest();
|
|
C_DrawConsole();
|
|
M_Drawer();
|
|
}
|
|
else
|
|
{
|
|
switch (gamestate)
|
|
{
|
|
case GS_FULLCONSOLE:
|
|
C_DrawConsole ();
|
|
M_Drawer ();
|
|
if (!screenshot)
|
|
screen->Update ();
|
|
return;
|
|
|
|
case GS_LEVEL:
|
|
case GS_TITLELEVEL:
|
|
if (!gametic)
|
|
break;
|
|
|
|
R_RefreshViewBorder ();
|
|
R_RenderActorView (players[consoleplayer].mo);
|
|
R_DetailDouble (); // [RH] Apply detail mode expansion
|
|
// [RH] Let cameras draw onto textures that were visible this frame.
|
|
FCanvasTextureInfo::UpdateAll ();
|
|
if (automapactive)
|
|
{
|
|
AM_Drawer ();
|
|
}
|
|
if (realviewheight == SCREENHEIGHT && viewactive)
|
|
{
|
|
StatusBar->Draw (DrawFSHUD ? HUD_Fullscreen : HUD_None);
|
|
StatusBar->DrawTopStuff (DrawFSHUD ? HUD_Fullscreen : HUD_None);
|
|
}
|
|
else
|
|
{
|
|
StatusBar->Draw (HUD_StatusBar);
|
|
StatusBar->DrawTopStuff (HUD_StatusBar);
|
|
}
|
|
CT_Drawer ();
|
|
break;
|
|
|
|
case GS_INTERMISSION:
|
|
WI_Drawer ();
|
|
CT_Drawer ();
|
|
break;
|
|
|
|
case GS_FINALE:
|
|
F_Drawer ();
|
|
CT_Drawer ();
|
|
break;
|
|
|
|
case GS_DEMOSCREEN:
|
|
D_PageDrawer ();
|
|
CT_Drawer ();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// draw pause pic
|
|
if (paused && menuactive == MENU_Off)
|
|
{
|
|
FTexture *tex;
|
|
int x;
|
|
|
|
tex = TexMan[gameinfo.gametype & (GAME_Doom|GAME_Strife) ? "M_PAUSE" : "PAUSED"];
|
|
x = (SCREENWIDTH - tex->GetWidth()*CleanXfac)/2 +
|
|
tex->LeftOffset*CleanXfac;
|
|
screen->DrawTexture (tex, x, 4, DTA_CleanNoMove, true, TAG_DONE);
|
|
}
|
|
|
|
// [RH] Draw icon, if any
|
|
if (D_DrawIcon)
|
|
{
|
|
int picnum = TexMan.CheckForTexture (D_DrawIcon, FTexture::TEX_MiscPatch);
|
|
|
|
D_DrawIcon = NULL;
|
|
if (picnum >= 0)
|
|
{
|
|
FTexture *tex = TexMan[picnum];
|
|
screen->DrawTexture (tex, 160-tex->GetWidth()/2, 100-tex->GetHeight()/2,
|
|
DTA_320x200, true, TAG_DONE);
|
|
}
|
|
NoWipe = 10;
|
|
}
|
|
|
|
NetUpdate (); // send out any new accumulation
|
|
|
|
if (!wipe || screenshot)
|
|
{
|
|
// normal update
|
|
C_DrawConsole (); // draw console
|
|
M_Drawer (); // menu is drawn even on top of everything
|
|
FStat::PrintStat ();
|
|
if (!screenshot)
|
|
screen->Update (); // page flip or blit buffer
|
|
}
|
|
else
|
|
{
|
|
// wipe update
|
|
int wipestart, nowtime, tics;
|
|
BOOL done;
|
|
|
|
wipe_EndScreen ();
|
|
screen->Unlock ();
|
|
|
|
wipestart = I_GetTime (false) - 1;
|
|
|
|
do
|
|
{
|
|
do
|
|
{
|
|
nowtime = I_GetTime (false);
|
|
tics = nowtime - wipestart;
|
|
} while (!tics);
|
|
wipestart = nowtime;
|
|
screen->Lock (true);
|
|
done = wipe_ScreenWipe (tics);
|
|
C_DrawConsole ();
|
|
M_Drawer (); // menu is drawn even on top of wipes
|
|
screen->Update (); // page flip or blit buffer
|
|
} while (!done);
|
|
}
|
|
|
|
unclock (cycles);
|
|
FrameCycles = cycles;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// DoConsoleAtExit
|
|
//
|
|
// Executes the contents of the atexit cvar, if any, at quit time.
|
|
//
|
|
//==========================================================================
|
|
|
|
static void STACK_ARGS DoConsoleAtExit ()
|
|
{
|
|
GameAtExit *cmd = ExitCmdList;
|
|
|
|
while (cmd != NULL)
|
|
{
|
|
GameAtExit *next = cmd->Next;
|
|
AddCommandString (cmd->Command);
|
|
free (cmd);
|
|
cmd = next;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD atexit
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (atexit)
|
|
{
|
|
if (argv.argc() == 1)
|
|
{
|
|
Printf ("Registered atexit commands:\n");
|
|
GameAtExit *record = ExitCmdList;
|
|
while (record != NULL)
|
|
{
|
|
Printf ("%s\n", record->Command);
|
|
record = record->Next;
|
|
}
|
|
return;
|
|
}
|
|
for (int i = 1; i < argv.argc(); ++i)
|
|
{
|
|
GameAtExit *record = (GameAtExit *)Malloc (
|
|
sizeof(GameAtExit)+strlen(argv[i]));
|
|
strcpy (record->Command, argv[i]);
|
|
record->Next = ExitCmdList;
|
|
ExitCmdList = record;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_ErrorCleanup ()
|
|
//
|
|
// Cleanup after a recoverable error.
|
|
//==========================================================================
|
|
|
|
void D_ErrorCleanup ()
|
|
{
|
|
screen->Unlock ();
|
|
bglobal.RemoveAllBots (true);
|
|
D_QuitNetGame ();
|
|
Net_ClearBuffers ();
|
|
G_NewInit ();
|
|
singletics = false;
|
|
if (demorecording || demoplayback)
|
|
G_CheckDemoStatus ();
|
|
playeringame[0] = 1;
|
|
players[0].playerstate = PST_LIVE;
|
|
gameaction = ga_fullconsole;
|
|
menuactive = MENU_Off;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_DoomLoop
|
|
//
|
|
// Manages timing and IO, calls all ?_Responder, ?_Ticker, and ?_Drawer,
|
|
// calls I_GetTime, I_StartFrame, and I_StartTic
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_DoomLoop ()
|
|
{
|
|
int lasttic = 0;
|
|
|
|
for (;;)
|
|
{
|
|
try
|
|
{
|
|
// frame syncronous IO operations
|
|
if (gametic > lasttic)
|
|
{
|
|
lasttic = gametic;
|
|
I_StartFrame ();
|
|
}
|
|
|
|
// process one or more tics
|
|
if (singletics)
|
|
{
|
|
I_StartTic ();
|
|
DObject::BeginFrame ();
|
|
D_ProcessEvents ();
|
|
G_BuildTiccmd (&netcmds[consoleplayer][maketic%BACKUPTICS]);
|
|
//Added by MC: For some of that bot stuff. The main bot function.
|
|
int i;
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (playeringame[i] && players[i].isbot && players[i].mo)
|
|
{
|
|
players[i].savedyaw = players[i].mo->angle;
|
|
players[i].savedpitch = players[i].mo->pitch;
|
|
}
|
|
}
|
|
bglobal.Main (maketic%BACKUPTICS);
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (playeringame[i] && players[i].isbot && players[i].mo)
|
|
{
|
|
players[i].mo->angle = players[i].savedyaw;
|
|
players[i].mo->pitch = players[i].savedpitch;
|
|
}
|
|
}
|
|
if (advancedemo)
|
|
D_DoAdvanceDemo ();
|
|
C_Ticker ();
|
|
M_Ticker ();
|
|
G_Ticker ();
|
|
gametic++;
|
|
maketic++;
|
|
DObject::EndFrame ();
|
|
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.
|
|
I_StartTic ();
|
|
D_Display (false);
|
|
}
|
|
catch (CRecoverableError &error)
|
|
{
|
|
if (error.GetMessage ())
|
|
{
|
|
Printf (PRINT_BOLD, "\n%s\n", error.GetMessage());
|
|
}
|
|
D_ErrorCleanup ();
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_PageTicker
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_PageTicker (void)
|
|
{
|
|
if (--pagetic < 0)
|
|
D_AdvanceDemo ();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_PageDrawer
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_PageDrawer (void)
|
|
{
|
|
if (Page != NULL)
|
|
{
|
|
screen->DrawTexture (Page, 0, 0,
|
|
DTA_VirtualWidth, Page->GetWidth(),
|
|
DTA_VirtualHeight, Page->GetHeight(),
|
|
DTA_Masked, false,
|
|
TAG_DONE);
|
|
screen->FillBorder (NULL);
|
|
}
|
|
else
|
|
{
|
|
screen->Clear (0, 0, SCREENWIDTH, SCREENHEIGHT, 0);
|
|
if (!PageBlank)
|
|
{
|
|
screen->DrawText (CR_WHITE, 0, 0, "Page graphic goes here", TAG_DONE);
|
|
}
|
|
}
|
|
if (Advisory != NULL)
|
|
{
|
|
screen->DrawTexture (Advisory, 4, 160, DTA_320x200, true, TAG_DONE);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_AdvanceDemo
|
|
//
|
|
// Called after each demo or intro demosequence finishes
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_AdvanceDemo (void)
|
|
{
|
|
advancedemo = true;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_DoStrifeAdvanceDemo
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_DoStrifeAdvanceDemo ()
|
|
{
|
|
static const char *const fullVoices[6] =
|
|
{
|
|
"svox/pro1", "svox/pro2", "svox/pro3", "svox/pro4", "svox/pro5", "svox/pro6"
|
|
};
|
|
static const char *const teaserVoices[6] =
|
|
{
|
|
"svox/voc91", "svox/voc92", "svox/voc93", "svox/voc94", "svox/voc95", "svox/voc96"
|
|
};
|
|
const char *const *voices = gameinfo.flags & GI_SHAREWARE ? teaserVoices : fullVoices;
|
|
const char *pagename = NULL;
|
|
|
|
gamestate = GS_DEMOSCREEN;
|
|
PageBlank = false;
|
|
|
|
switch (demosequence)
|
|
{
|
|
default:
|
|
case 0:
|
|
pagetic = 6 * TICRATE;
|
|
pagename = "TITLEPIC";
|
|
if (Wads.CheckNumForName ("d_logo") < 0)
|
|
{ // strife0.wad does not have d_logo
|
|
S_StartMusic ("");
|
|
}
|
|
else
|
|
{
|
|
S_StartMusic ("d_logo");
|
|
}
|
|
C_HideConsole ();
|
|
break;
|
|
|
|
case 1:
|
|
// [RH] Strife fades to black and then to the Rogue logo, but
|
|
// I think it looks better if it doesn't fade.
|
|
pagetic = 10 * TICRATE/35;
|
|
pagename = ""; // PANEL0, but strife0.wad doesn't have it, so don't use it.
|
|
PageBlank = true;
|
|
S_Sound (CHAN_VOICE, "bishop/active", 1, ATTN_NORM);
|
|
break;
|
|
|
|
case 2:
|
|
pagetic = 4 * TICRATE;
|
|
pagename = "RGELOGO";
|
|
break;
|
|
|
|
case 3:
|
|
pagetic = 7 * TICRATE;
|
|
pagename = "PANEL1";
|
|
S_Sound (CHAN_VOICE, voices[0], 1, ATTN_NORM);
|
|
S_StartMusic ("d_intro");
|
|
break;
|
|
|
|
case 4:
|
|
pagetic = 9 * TICRATE;
|
|
pagename = "PANEL2";
|
|
S_Sound (CHAN_VOICE, voices[1], 1, ATTN_NORM);
|
|
break;
|
|
|
|
case 5:
|
|
pagetic = 12 * TICRATE;
|
|
pagename = "PANEL3";
|
|
S_Sound (CHAN_VOICE, voices[2], 1, ATTN_NORM);
|
|
break;
|
|
|
|
case 6:
|
|
pagetic = 11 * TICRATE;
|
|
pagename = "PANEL4";
|
|
S_Sound (CHAN_VOICE, voices[3], 1, ATTN_NORM);
|
|
break;
|
|
|
|
case 7:
|
|
pagetic = 10 * TICRATE;
|
|
pagename = "PANEL5";
|
|
S_Sound (CHAN_VOICE, voices[4], 1, ATTN_NORM);
|
|
break;
|
|
|
|
case 8:
|
|
pagetic = 16 * TICRATE;
|
|
pagename = "PANEL6";
|
|
S_Sound (CHAN_VOICE, voices[5], 1, ATTN_NORM);
|
|
break;
|
|
|
|
case 9:
|
|
pagetic = 6 * TICRATE;
|
|
pagename = "vellogo";
|
|
wipegamestate = GS_FORCEWIPEFADE;
|
|
break;
|
|
|
|
case 10:
|
|
pagetic = 12 * TICRATE;
|
|
pagename = "CREDIT";
|
|
wipegamestate = GS_FORCEWIPEFADE;
|
|
break;
|
|
}
|
|
if (demosequence++ > 10)
|
|
demosequence = 0;
|
|
if (demosequence == 9 && !(gameinfo.flags & GI_SHAREWARE))
|
|
demosequence = 10;
|
|
|
|
if (pagename)
|
|
{
|
|
if (Page != NULL)
|
|
{
|
|
Page->Unload ();
|
|
Page = NULL;
|
|
}
|
|
if (pagename[0])
|
|
{
|
|
Page = TexMan[pagename];
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_DoAdvanceDemo
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_DoAdvanceDemo (void)
|
|
{
|
|
static char demoname[8] = "DEMO1";
|
|
static int democount = 0;
|
|
static int pagecount;
|
|
const char *pagename = NULL;
|
|
|
|
V_SetBlend (0,0,0,0);
|
|
players[consoleplayer].playerstate = PST_LIVE; // not reborn
|
|
advancedemo = false;
|
|
usergame = false; // no save / end game here
|
|
paused = 0;
|
|
gameaction = ga_nothing;
|
|
|
|
// [RH] If you want something more dynamic for your title, create a map
|
|
// and name it TITLEMAP. That map will be loaded and used as the title.
|
|
if (Wads.CheckNumForName ("TITLEMAP") >= 0)
|
|
{
|
|
G_InitNew ("TITLEMAP", true);
|
|
return;
|
|
}
|
|
|
|
if (gameinfo.gametype == GAME_Strife)
|
|
{
|
|
D_DoStrifeAdvanceDemo ();
|
|
return;
|
|
}
|
|
|
|
switch (demosequence)
|
|
{
|
|
case 3:
|
|
if (gameinfo.advisoryTime)
|
|
{
|
|
Advisory = TexMan["ADVISOR"];
|
|
demosequence = 1;
|
|
pagetic = (int)(gameinfo.advisoryTime * TICRATE);
|
|
break;
|
|
}
|
|
// fall through to case 1 if no advisory notice
|
|
|
|
case 1:
|
|
Advisory = NULL;
|
|
if (!M_DemoNoPlay)
|
|
{
|
|
BorderNeedRefresh = screen->GetPageCount ();
|
|
democount++;
|
|
sprintf (demoname + 4, "%d", democount);
|
|
if (Wads.CheckNumForName (demoname) < 0)
|
|
{
|
|
demosequence = 0;
|
|
democount = 0;
|
|
// falls through to case 0 below
|
|
}
|
|
else
|
|
{
|
|
G_DeferedPlayDemo (demoname);
|
|
demosequence = 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
default:
|
|
case 0:
|
|
gamestate = GS_DEMOSCREEN;
|
|
pagename = gameinfo.titlePage;
|
|
pagetic = (int)(gameinfo.titleTime * TICRATE);
|
|
S_StartMusic (gameinfo.titleMusic);
|
|
demosequence = 3;
|
|
pagecount = 0;
|
|
C_HideConsole ();
|
|
break;
|
|
|
|
case 2:
|
|
pagetic = (int)(gameinfo.pageTime * TICRATE);
|
|
gamestate = GS_DEMOSCREEN;
|
|
if (pagecount == 0)
|
|
pagename = gameinfo.creditPage1;
|
|
else
|
|
pagename = gameinfo.creditPage2;
|
|
pagecount ^= 1;
|
|
demosequence = 1;
|
|
break;
|
|
}
|
|
|
|
if (pagename)
|
|
{
|
|
if (Page != NULL)
|
|
{
|
|
Page->Unload ();
|
|
}
|
|
Page = TexMan[pagename];
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_StartTitle
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_StartTitle (void)
|
|
{
|
|
gameaction = ga_nothing;
|
|
demosequence = -1;
|
|
D_AdvanceDemo ();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Cmd_Endgame
|
|
//
|
|
// [RH] Quit the current game and go to fullscreen console
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (endgame)
|
|
{
|
|
if (!netgame)
|
|
{
|
|
gameaction = ga_fullconsole;
|
|
demosequence = -1;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_AddFile
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_AddFile (const char *file)
|
|
{
|
|
if (file == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!FileExists (file))
|
|
{
|
|
const char *f = BaseFileSearch (file, ".wad");
|
|
if (f == NULL)
|
|
{
|
|
Printf ("Can't find '%s'\n", file);
|
|
return;
|
|
}
|
|
file = f;
|
|
}
|
|
wadlist_t *wad = (wadlist_t *)Malloc (sizeof(*wad) + strlen(file));
|
|
|
|
*wadtail = wad;
|
|
wad->next = NULL;
|
|
strcpy (wad->name, file);
|
|
wadtail = &wad->next;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_AddWildFile
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_AddWildFile (const char *value)
|
|
{
|
|
const char *wadfile = BaseFileSearch (value, ".wad");
|
|
|
|
if (wadfile != NULL)
|
|
{
|
|
D_AddFile (wadfile);
|
|
}
|
|
else
|
|
{ // Try pattern matching
|
|
findstate_t findstate;
|
|
char path[PATH_MAX];
|
|
char *sep;
|
|
void *handle = I_FindFirst (value, &findstate);
|
|
|
|
strcpy (path, value);
|
|
sep = strrchr (path, '/');
|
|
if (sep == NULL)
|
|
{
|
|
sep = strrchr (path, '\\');
|
|
#ifdef _WIN32
|
|
if (sep == NULL && path[1] == ':')
|
|
{
|
|
sep = path + 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (handle != ((void *)-1))
|
|
{
|
|
do
|
|
{
|
|
if (sep == NULL)
|
|
{
|
|
D_AddFile (I_FindName (&findstate));
|
|
}
|
|
else
|
|
{
|
|
strcpy (sep+1, I_FindName (&findstate));
|
|
D_AddFile (path);
|
|
}
|
|
} while (I_FindNext (handle, &findstate) == 0);
|
|
}
|
|
I_FindClose (handle);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_AddConfigWads
|
|
//
|
|
// Adds all files in the specified config file section.
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_AddConfigWads (const char *section)
|
|
{
|
|
if (GameConfig->SetSection (section))
|
|
{
|
|
const char *key;
|
|
const char *value;
|
|
FConfigFile::Position pos;
|
|
|
|
while (GameConfig->NextInSection (key, value))
|
|
{
|
|
if (stricmp (key, "Path") == 0)
|
|
{
|
|
// D_AddWildFile resets GameConfig's position, so remember it
|
|
GameConfig->GetPosition (pos);
|
|
D_AddWildFile (value);
|
|
// Reset GameConfig's position to get next wad
|
|
GameConfig->SetPosition (pos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_AddDirectory
|
|
//
|
|
// Add all .wad files in a directory. Does not descend into subdirectories.
|
|
//
|
|
//==========================================================================
|
|
|
|
static void D_AddDirectory (const char *dir)
|
|
{
|
|
char curdir[PATH_MAX];
|
|
|
|
if (getcwd (curdir, PATH_MAX))
|
|
{
|
|
char skindir[PATH_MAX];
|
|
findstate_t findstate;
|
|
void *handle;
|
|
size_t stuffstart;
|
|
|
|
stuffstart = strlen (dir);
|
|
memcpy (skindir, dir, stuffstart*sizeof(*dir));
|
|
skindir[stuffstart] = 0;
|
|
|
|
if (skindir[stuffstart-1] == '/')
|
|
{
|
|
skindir[--stuffstart] = 0;
|
|
}
|
|
|
|
if (!chdir (skindir))
|
|
{
|
|
skindir[stuffstart++] = '/';
|
|
if ((handle = I_FindFirst ("*.wad", &findstate)) != (void *)-1)
|
|
{
|
|
do
|
|
{
|
|
if (!(I_FindAttr (&findstate) & FA_DIREC))
|
|
{
|
|
strcpy (skindir + stuffstart, I_FindName (&findstate));
|
|
D_AddFile (skindir);
|
|
}
|
|
} while (I_FindNext (handle, &findstate) == 0);
|
|
I_FindClose (handle);
|
|
}
|
|
}
|
|
chdir (curdir);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// SetIWAD
|
|
//
|
|
// Sets parameters for the game using the specified IWAD.
|
|
//==========================================================================
|
|
|
|
static void SetIWAD (const char *iwadpath, EIWADType type)
|
|
{
|
|
static const struct
|
|
{
|
|
GameMode_t Mode;
|
|
const gameinfo_t *Info;
|
|
GameMission_t Mission;
|
|
} Datas[NUM_IWAD_TYPES] = {
|
|
{ commercial, &CommercialGameInfo, pack_tnt }, // Doom2TNT
|
|
{ commercial, &CommercialGameInfo, pack_plut }, // Doom2Plutonia
|
|
{ commercial, &HexenGameInfo, doom2 }, // Hexen
|
|
{ commercial, &HexenDKGameInfo, doom2 }, // HexenDK
|
|
{ commercial, &CommercialGameInfo, doom2 }, // Doom2
|
|
{ shareware, &HereticSWGameInfo, doom }, // HereticShareware
|
|
{ retail, &HereticGameInfo, doom }, // HereticExtended
|
|
{ retail, &HereticGameInfo, doom }, // Heretic
|
|
{ shareware, &SharewareGameInfo, doom }, // DoomShareware
|
|
{ retail, &RetailGameInfo, doom }, // UltimateDoom
|
|
{ registered, &RegisteredGameInfo, doom }, // DoomRegistered
|
|
{ commercial, &StrifeGameInfo, doom2 }, // Strife
|
|
{ commercial, &StrifeTeaserGameInfo, doom2 }, // StrifeTeaser
|
|
{ commercial, &StrifeTeaser2GameInfo, doom2 }, // StrifeTeaser2
|
|
};
|
|
|
|
D_AddFile (iwadpath);
|
|
|
|
if ((unsigned)type < NUM_IWAD_TYPES)
|
|
{
|
|
gamemode = Datas[type].Mode;
|
|
gameinfo = *Datas[type].Info;
|
|
gamemission = Datas[type].Mission;
|
|
if (type == IWAD_HereticExtended)
|
|
{
|
|
gameinfo.flags |= GI_MENUHACK_EXTENDED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gamemode = undetermined;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// ScanIWAD
|
|
//
|
|
// Scan the contents of an IWAD to determine which one it is
|
|
//==========================================================================
|
|
|
|
static EIWADType ScanIWAD (const char *iwad)
|
|
{
|
|
static const char checklumps[][8] =
|
|
{
|
|
"E1M1",
|
|
"E4M1",
|
|
"MAP01",
|
|
"MAP60",
|
|
"TITLE",
|
|
"REDTNT2",
|
|
"CAMO1",
|
|
{ 'E','X','T','E','N','D','E','D'},
|
|
"ENDSTRF",
|
|
"MAP33",
|
|
"INVCURS",
|
|
"E2M1","E2M2","E2M3","E2M4","E2M5","E2M6","E2M7","E2M8","E2M9",
|
|
"E3M1","E3M2","E3M3","E3M4","E3M5","E3M6","E3M7","E3M8","E3M9",
|
|
"DPHOOF","BFGGA0","HEADA1","CYBRA1",
|
|
{ 'S','P','I','D','A','1','D','1' },
|
|
};
|
|
#define NUM_CHECKLUMPS (sizeof(checklumps)/8)
|
|
enum
|
|
{
|
|
Check_e1m1,
|
|
Check_e4m1,
|
|
Check_map01,
|
|
Check_map60,
|
|
Check_title,
|
|
Check_redtnt2,
|
|
Check_cam01,
|
|
Check_Extended,
|
|
Check_endstrf,
|
|
Check_map33,
|
|
Check_invcurs,
|
|
Check_e2m1
|
|
};
|
|
int lumpsfound[NUM_CHECKLUMPS];
|
|
size_t i;
|
|
wadinfo_t header;
|
|
FILE *f;
|
|
|
|
memset (lumpsfound, 0, sizeof(lumpsfound));
|
|
if ( (f = fopen (iwad, "rb")) )
|
|
{
|
|
fread (&header, sizeof(header), 1, f);
|
|
if (header.Magic == IWAD_ID || header.Magic == PWAD_ID)
|
|
{
|
|
header.NumLumps = LittleLong(header.NumLumps);
|
|
if (0 == fseek (f, LittleLong(header.InfoTableOfs), SEEK_SET))
|
|
{
|
|
for (i = 0; i < (size_t)header.NumLumps; i++)
|
|
{
|
|
wadlump_t lump;
|
|
size_t j;
|
|
|
|
if (0 == fread (&lump, sizeof(lump), 1, f))
|
|
break;
|
|
for (j = 0; j < NUM_CHECKLUMPS; j++)
|
|
if (strnicmp (lump.Name, checklumps[j], 8) == 0)
|
|
lumpsfound[j]++;
|
|
}
|
|
}
|
|
}
|
|
fclose (f);
|
|
}
|
|
|
|
if (lumpsfound[Check_title] && lumpsfound[Check_map60])
|
|
{
|
|
return IWAD_HexenDK;
|
|
}
|
|
else if (lumpsfound[Check_map33] && lumpsfound[Check_endstrf])
|
|
{
|
|
if (lumpsfound[Check_map01])
|
|
{
|
|
return IWAD_Strife;
|
|
}
|
|
else if (lumpsfound[Check_invcurs])
|
|
{
|
|
return IWAD_StrifeTeaser2;
|
|
}
|
|
else
|
|
{
|
|
return IWAD_StrifeTeaser;
|
|
}
|
|
}
|
|
else if (lumpsfound[Check_map01])
|
|
{
|
|
if (lumpsfound[Check_redtnt2])
|
|
{
|
|
return IWAD_Doom2TNT;
|
|
}
|
|
else if (lumpsfound[Check_cam01])
|
|
{
|
|
return IWAD_Doom2Plutonia;
|
|
}
|
|
else
|
|
{
|
|
if (lumpsfound[Check_title])
|
|
{
|
|
return IWAD_Hexen;
|
|
}
|
|
else
|
|
{
|
|
return IWAD_Doom2;
|
|
}
|
|
}
|
|
}
|
|
else if (lumpsfound[Check_e1m1])
|
|
{
|
|
if (lumpsfound[Check_title])
|
|
{
|
|
if (!lumpsfound[Check_e2m1])
|
|
{
|
|
return IWAD_HereticShareware;
|
|
}
|
|
else
|
|
{
|
|
if (lumpsfound[Check_Extended])
|
|
{
|
|
return IWAD_HereticExtended;
|
|
}
|
|
else
|
|
{
|
|
return IWAD_Heretic;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = Check_e2m1; i < NUM_CHECKLUMPS; i++)
|
|
{
|
|
if (!lumpsfound[i])
|
|
{
|
|
return IWAD_DoomShareware;
|
|
}
|
|
}
|
|
if (i == NUM_CHECKLUMPS)
|
|
{
|
|
if (lumpsfound[Check_e4m1])
|
|
{
|
|
return IWAD_UltimateDoom;
|
|
}
|
|
else
|
|
{
|
|
return IWAD_DoomRegistered;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NUM_IWAD_TYPES; // Don't know
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CheckIWAD
|
|
//
|
|
// Tries to find an IWAD from a set of known IWAD names, and checks the
|
|
// contents of each one found to determine which game it belongs to.
|
|
// Returns the number of new wads found in this pass (does not count wads
|
|
// found from a previous call).
|
|
//
|
|
//==========================================================================
|
|
|
|
static int CheckIWAD (const char *doomwaddir, WadStuff *wads)
|
|
{
|
|
const char *slash;
|
|
char iwad[512];
|
|
int i;
|
|
int numfound;
|
|
|
|
numfound = 0;
|
|
|
|
slash = (doomwaddir[0] && doomwaddir[strlen (doomwaddir)-1] != '/') ? "/" : "";
|
|
|
|
// Search for a pre-defined IWAD
|
|
for (i = IWADNames[0] ? 0 : 1; IWADNames[i]; i++)
|
|
{
|
|
if (wads[i].Path == NULL)
|
|
{
|
|
sprintf (iwad, "%s%s%s", doomwaddir, slash, IWADNames[i]);
|
|
FixPathSeperator (iwad);
|
|
if (FileExists (iwad))
|
|
{
|
|
wads[i].Type = ScanIWAD (iwad);
|
|
if (wads[i].Type != NUM_IWAD_TYPES)
|
|
{
|
|
wads[i].Path = copystring (iwad);
|
|
numfound++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return numfound;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CheckIWADinEnvDir
|
|
//
|
|
// Checks for an IWAD in a directory specified in an environment variable.
|
|
//
|
|
//==========================================================================
|
|
|
|
static int CheckIWADinEnvDir (const char *envname, WadStuff *wads)
|
|
{
|
|
char dir[512];
|
|
const char *envdir = getenv (envname);
|
|
|
|
if (envdir)
|
|
{
|
|
strcpy (dir, envdir);
|
|
FixPathSeperator (dir);
|
|
if (dir[strlen(dir) - 1] != '/')
|
|
strcat (dir, "/");
|
|
return CheckIWAD (dir, wads);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// IdentifyVersion
|
|
//
|
|
// Tries to find an IWAD in one of four directories under DOS or Win32:
|
|
// 1. Current directory
|
|
// 2. Executable directory
|
|
// 3. $DOOMWADDIR
|
|
// 4. $HOME
|
|
//
|
|
// Under UNIX OSes, the search path is:
|
|
// 1. Current directory
|
|
// 2. $DOOMWADDIR
|
|
// 3. $HOME/.zdoom
|
|
// 4. The share directory defined at compile time (/usr/local/share/zdoom)
|
|
//
|
|
// The search path can be altered by editing the IWADSearch.Directories
|
|
// section of the config file.
|
|
//
|
|
//==========================================================================
|
|
|
|
static EIWADType IdentifyVersion (void)
|
|
{
|
|
WadStuff wads[sizeof(IWADNames)/sizeof(char *)];
|
|
size_t foundwads[NUM_IWAD_TYPES] = { 0 };
|
|
const char *iwadparm = Args.CheckValue ("-iwad");
|
|
size_t numwads;
|
|
int pickwad;
|
|
size_t i;
|
|
bool iwadparmfound = false;
|
|
|
|
memset (wads, 0, sizeof(wads));
|
|
|
|
if (iwadparm)
|
|
{
|
|
char *custwad = new char[strlen (iwadparm) + 5];
|
|
strcpy (custwad, iwadparm);
|
|
FixPathSeperator (custwad);
|
|
if (CheckIWAD (custwad, wads))
|
|
{ // -iwad parameter was a directory
|
|
delete[] custwad;
|
|
iwadparm = NULL;
|
|
}
|
|
else
|
|
{
|
|
DefaultExtension (custwad, ".wad");
|
|
iwadparm = custwad;
|
|
IWADNames[0] = iwadparm;
|
|
CheckIWAD ("", wads);
|
|
}
|
|
}
|
|
|
|
if (iwadparm == NULL || wads[0].Path == NULL)
|
|
{
|
|
if (GameConfig->SetSection ("IWADSearch.Directories"))
|
|
{
|
|
const char *key;
|
|
const char *value;
|
|
|
|
while (GameConfig->NextInSection (key, value))
|
|
{
|
|
if (stricmp (key, "Path") == 0)
|
|
{
|
|
if (*value == '$')
|
|
{
|
|
if (stricmp (value + 1, "progdir") == 0)
|
|
{
|
|
CheckIWAD (progdir, wads);
|
|
}
|
|
else
|
|
{
|
|
CheckIWADinEnvDir (value + 1, wads);
|
|
}
|
|
}
|
|
#ifdef unix
|
|
else if (*value == '~' && (*(value + 1) == 0 || *(value + 1) == '/'))
|
|
{
|
|
string homepath = GetUserFile (*(value + 1) ? value + 2 : value + 1);
|
|
CheckIWAD (homepath.GetChars(), wads);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
CheckIWAD (value, wads);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iwadparm != NULL && wads[0].Path != NULL)
|
|
{
|
|
iwadparmfound = true;
|
|
}
|
|
|
|
for (i = numwads = 0; i < sizeof(IWADNames)/sizeof(char *); i++)
|
|
{
|
|
if (wads[i].Path != NULL)
|
|
{
|
|
if (i != numwads)
|
|
{
|
|
wads[numwads] = wads[i];
|
|
}
|
|
foundwads[wads[numwads].Type] = numwads+1;
|
|
numwads++;
|
|
}
|
|
}
|
|
|
|
if (foundwads[IWAD_HexenDK] && !foundwads[IWAD_Hexen])
|
|
{ // Cannot play Hexen DK without Hexen
|
|
size_t kill = foundwads[IWAD_HexenDK];
|
|
if (kill != numwads)
|
|
{
|
|
memmove (&wads[kill-1], &wads[kill], numwads - kill);
|
|
}
|
|
numwads--;
|
|
}
|
|
|
|
if (numwads == 0)
|
|
{
|
|
I_FatalError ("Cannot find a game IWAD (doom.wad, doom2.wad, heretic.wad, etc.).\n"
|
|
"Did you install ZDoom properly? You can do either of the following:\n"
|
|
"\n"
|
|
"1. Place one or more of these wads in the same directory as ZDoom.\n"
|
|
"2. Edit your zdoom.ini and add the directories of your iwads\n"
|
|
"to the list beneath [IWADSearch.Directories]");
|
|
}
|
|
|
|
pickwad = 0;
|
|
|
|
if (!iwadparmfound && numwads > 1 && queryiwad)
|
|
{
|
|
pickwad = I_PickIWad (wads, (int)numwads);
|
|
}
|
|
|
|
if (pickwad < 0)
|
|
exit (0);
|
|
|
|
if (wads[pickwad].Type == IWAD_HexenDK)
|
|
{ // load hexen.wad before loading hexdd.wad
|
|
D_AddFile (wads[foundwads[IWAD_Hexen]-1].Path);
|
|
}
|
|
|
|
SetIWAD (wads[pickwad].Path, wads[pickwad].Type);
|
|
|
|
if (wads[pickwad].Type == IWAD_Strife)
|
|
{ // Try to load voices.wad along with strife1.wad
|
|
char *filepart = strrchr (wads[pickwad].Path, '/');
|
|
if (filepart == NULL)
|
|
{
|
|
filepart = wads[pickwad].Path;
|
|
}
|
|
else
|
|
{
|
|
++filepart;
|
|
}
|
|
strcpy (filepart, "voices.wad");
|
|
D_AddFile (wads[pickwad].Path);
|
|
}
|
|
|
|
for (i = 0; i < numwads; i++)
|
|
{
|
|
delete[] wads[i].Path;
|
|
}
|
|
|
|
return wads[pickwad].Type;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// BaseFileSearch
|
|
//
|
|
// If a file does not exist at <file>, looks for it in the directories
|
|
// specified in the config file. Returns the path to the file, if found,
|
|
// or NULL if it could not be found.
|
|
//
|
|
//==========================================================================
|
|
|
|
static const char *BaseFileSearch (const char *file, const char *ext)
|
|
{
|
|
static char wad[PATH_MAX];
|
|
|
|
if (FileExists (file))
|
|
{
|
|
return file;
|
|
}
|
|
|
|
if (GameConfig->SetSection ("FileSearch.Directories"))
|
|
{
|
|
const char *key;
|
|
const char *value;
|
|
|
|
while (GameConfig->NextInSection (key, value))
|
|
{
|
|
if (stricmp (key, "Path") == 0)
|
|
{
|
|
const char *dir;
|
|
string homepath;
|
|
|
|
if (*value == '$')
|
|
{
|
|
if (stricmp (value + 1, "progdir") == 0)
|
|
{
|
|
dir = progdir;
|
|
}
|
|
else
|
|
{
|
|
dir = getenv (value + 1);
|
|
}
|
|
}
|
|
#ifdef unix
|
|
else if (*value == '~' && (*(value + 1) == 0 || *(value + 1) == '/'))
|
|
{
|
|
homepath = GetUserFile (*(value + 1) ? value + 2 : value + 1);
|
|
dir = homepath.GetChars();
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
dir = value;
|
|
}
|
|
if (dir != NULL)
|
|
{
|
|
sprintf (wad, "%s%s%s", dir, dir[strlen (dir) - 1] != '/' ? "/" : "", file);
|
|
if (FileExists (wad))
|
|
{
|
|
return wad;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Retry, this time with a default extension
|
|
if (ext != NULL)
|
|
{
|
|
string tmp = file;
|
|
DefaultExtension (tmp, ext);
|
|
return BaseFileSearch (tmp.GetChars(), NULL);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// ConsiderPatches
|
|
//
|
|
// Tries to add any deh/bex patches from the command line.
|
|
//
|
|
//==========================================================================
|
|
|
|
bool ConsiderPatches (const char *arg, const char *ext)
|
|
{
|
|
bool noDef = false;
|
|
DArgs *files = Args.GatherFiles (arg, ext, false);
|
|
|
|
if (files->NumArgs() > 0)
|
|
{
|
|
int i;
|
|
const char *f;
|
|
|
|
for (i = 0; i < files->NumArgs(); ++i)
|
|
{
|
|
if ( (f = BaseFileSearch (files->GetArg (i), ".deh")) )
|
|
DoDehPatch (f, false);
|
|
else if ( (f = BaseFileSearch (files->GetArg (i), ".bex")) )
|
|
DoDehPatch (f, false);
|
|
}
|
|
noDef = true;
|
|
}
|
|
delete files;
|
|
return noDef;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_LoadWadSettings
|
|
//
|
|
// Parses any loaded KEYCONF lumps. These are restricted console scripts
|
|
// that can only execute the alias, defaultbind, addkeysection,
|
|
// addmenukey, weaponsection, and addslotdefault commands.
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_LoadWadSettings ()
|
|
{
|
|
char cmd[4096];
|
|
int lump, lastlump = 0;
|
|
|
|
ParsingKeyConf = true;
|
|
|
|
while ((lump = Wads.FindLump ("KEYCONF", &lastlump)) != -1)
|
|
{
|
|
FMemLump data = Wads.ReadLump (lump);
|
|
const char *eof = (char *)data.GetMem() + Wads.LumpLength (lump);
|
|
const char *conf = (char *)data.GetMem();
|
|
|
|
while (conf < eof)
|
|
{
|
|
size_t i;
|
|
|
|
// Fetch a line to execute
|
|
for (i = 0; conf + i < eof && conf[i] != '\n'; ++i)
|
|
{
|
|
cmd[i] = conf[i];
|
|
}
|
|
cmd[i] = 0;
|
|
conf += i;
|
|
if (*conf == '\n')
|
|
{
|
|
conf++;
|
|
}
|
|
|
|
// Comments begin with //
|
|
char *stop = cmd + i - 1;
|
|
char *comment = cmd;
|
|
int inQuote = 0;
|
|
|
|
if (*stop == '\r')
|
|
*stop-- = 0;
|
|
|
|
while (comment < stop)
|
|
{
|
|
if (*comment == '\"')
|
|
{
|
|
inQuote ^= 1;
|
|
}
|
|
else if (!inQuote && *comment == '/' && *(comment + 1) == '/')
|
|
{
|
|
break;
|
|
}
|
|
comment++;
|
|
}
|
|
if (comment == cmd)
|
|
{ // Comment at line beginning
|
|
continue;
|
|
}
|
|
else if (comment < stop)
|
|
{ // Comment in middle of line
|
|
*comment = 0;
|
|
}
|
|
|
|
AddCommandString (cmd);
|
|
}
|
|
}
|
|
ParsingKeyConf = false;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_MultiExec
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_MultiExec (DArgs *list, bool usePullin)
|
|
{
|
|
for (int i = 0; i < list->NumArgs(); ++i)
|
|
{
|
|
C_ExecFile (list->GetArg (i), usePullin);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// D_DoomMain
|
|
//
|
|
//==========================================================================
|
|
|
|
void D_DoomMain (void)
|
|
{
|
|
int p, flags;
|
|
char file[PATH_MAX];
|
|
char *v;
|
|
const char *wad;
|
|
DArgs *execFiles;
|
|
|
|
file[PATH_MAX-1] = 0;
|
|
|
|
#if defined(_MSC_VER) || defined(__GNUC__)
|
|
TypeInfo::StaticInit ();
|
|
#endif
|
|
|
|
atterm (DObject::StaticShutdown);
|
|
atterm (DoConsoleAtExit);
|
|
|
|
gamestate = GS_STARTUP;
|
|
|
|
SetLanguageIDs ();
|
|
|
|
rngseed = (DWORD)time (NULL);
|
|
FRandom::StaticClearRandom ();
|
|
M_FindResponseFile ();
|
|
M_LoadDefaults (); // load before initing other systems
|
|
|
|
// [RH] Make sure zdoom.wad is always loaded,
|
|
// as it contains magic stuff we need.
|
|
wad = BaseFileSearch ("zdoom.wad", NULL);
|
|
if (wad)
|
|
D_AddFile (wad);
|
|
else
|
|
I_FatalError ("Cannot find zdoom.wad");
|
|
|
|
I_SetTitleString (IWADTypeNames[IdentifyVersion ()]);
|
|
GameConfig->DoGameSetup (GameNames[gameinfo.gametype]);
|
|
|
|
// [RH] zvox.wad - A wad I had intended to be automatically generated
|
|
// from Q2's pak0.pak so the female and cyborg player could have
|
|
// voices. I never got around to writing the utility to do it, though.
|
|
wad = BaseFileSearch ("zvox.wad", NULL);
|
|
if (wad)
|
|
D_AddFile (wad);
|
|
|
|
// [RH] Add any .wad files in the skins directory
|
|
#ifdef unix
|
|
sprintf (file, "%sskins", SHARE_DIR);
|
|
#else
|
|
sprintf (file, "%sskins", progdir);
|
|
#endif
|
|
D_AddDirectory (file);
|
|
|
|
const char *home = getenv ("HOME");
|
|
if (home)
|
|
{
|
|
sprintf (file, "%s%s.zdoom/skins", home,
|
|
home[strlen(home)-1] == '/' ? "" : "/");
|
|
D_AddDirectory (file);
|
|
}
|
|
|
|
if (!(gameinfo.flags & GI_SHAREWARE))
|
|
{
|
|
// Add common (global) wads
|
|
D_AddConfigWads ("Global.Autoload");
|
|
|
|
// Add game-specific wads
|
|
sprintf (file, "%s.Autoload", GameNames[gameinfo.gametype]);
|
|
D_AddConfigWads (file);
|
|
}
|
|
|
|
// Run automatically executed files
|
|
execFiles = new DArgs;
|
|
GameConfig->AddAutoexec (execFiles, GameNames[gameinfo.gametype]);
|
|
D_MultiExec (execFiles, true);
|
|
delete execFiles;
|
|
|
|
// Run .cfg files at the start of the command line.
|
|
execFiles = Args.GatherFiles (NULL, ".cfg", false);
|
|
D_MultiExec (execFiles, true);
|
|
delete execFiles;
|
|
|
|
C_ExecCmdLineParams (); // [RH] do all +set commands on the command line
|
|
|
|
DArgs *files = Args.GatherFiles ("-file", ".wad", true);
|
|
if (files->NumArgs() > 0)
|
|
{
|
|
// Check for -file in shareware
|
|
if (gameinfo.flags & GI_SHAREWARE)
|
|
{
|
|
I_FatalError ("You cannot -file with the shareware version. Register!");
|
|
}
|
|
|
|
// the files gathered are wadfile/lump names
|
|
for (int i = 0; i < files->NumArgs(); i++)
|
|
{
|
|
D_AddWildFile (files->GetArg (i));
|
|
}
|
|
}
|
|
delete files;
|
|
|
|
Wads.InitMultipleFiles (&wadfiles);
|
|
|
|
// [RH] Initialize localizable strings.
|
|
GStrings.LoadStrings (false);
|
|
|
|
//P_InitXlat ();
|
|
|
|
// [RH] Moved these up here so that we can do most of our
|
|
// startup output in a fullscreen console.
|
|
|
|
CT_Init ();
|
|
I_Init ();
|
|
V_Init ();
|
|
|
|
// Base systems have been inited; enable cvar callbacks
|
|
FBaseCVar::EnableCallbacks ();
|
|
|
|
|
|
// [RH] Now that all text strings are set up,
|
|
// insert them into the level and cluster data.
|
|
G_MakeEpisodes ();
|
|
|
|
// [RH] Parse through all loaded mapinfo lumps
|
|
G_ParseMapInfo ();
|
|
|
|
// [RH] Parse any SNDINFO lumps
|
|
S_ParseSndInfo ();
|
|
S_ParseSndEax ();
|
|
|
|
FActorInfo::StaticInit ();
|
|
|
|
// [RH] Load custom key and weapon settings from WADs
|
|
D_LoadWadSettings ();
|
|
|
|
FActorInfo::StaticGameSet ();
|
|
|
|
Printf ("Init DOOM refresh subsystem.\n");
|
|
R_Init ();
|
|
|
|
DecalLibrary.Clear ();
|
|
DecalLibrary.ReadAllDecals ();
|
|
|
|
// [RH] Try adding .deh and .bex files on the command line.
|
|
// If there are none, try adding any in the config file.
|
|
|
|
if (gameinfo.gametype == GAME_Doom)
|
|
{
|
|
if (!ConsiderPatches ("-deh", ".deh") &&
|
|
!ConsiderPatches ("-bex", ".bex") &&
|
|
GameConfig->SetSection ("Doom.DefaultDehacked"))
|
|
{
|
|
const char *key;
|
|
const char *value;
|
|
|
|
while (GameConfig->NextInSection (key, value))
|
|
{
|
|
if (stricmp (key, "Path") == 0 && FileExists (value))
|
|
{
|
|
Printf ("Applying patch %s\n", value);
|
|
DoDehPatch (value, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
DoDehPatch (NULL, true); // See if there's a patch in a PWAD
|
|
FinishDehPatch (); // Create replacements for dehacked pickups
|
|
}
|
|
HandleNoSector (); // clear NOSECTOR flag off all actors modified by Dehacked and the BossEye.
|
|
|
|
FActorInfo::StaticSetActorNums ();
|
|
|
|
// [RH] User-configurable startup strings. Because BOOM does.
|
|
static const char *startupString[5] = {
|
|
"STARTUP1", "STARTUP2", "STARTUP3", "STARTUP4", "STARTUP5"
|
|
};
|
|
for (p = 0; p < 5; ++p)
|
|
{
|
|
const char *str = GStrings[startupString[p]];
|
|
if (str != NULL && str[0] != '\0')
|
|
{
|
|
Printf ("%s\n", str);
|
|
}
|
|
}
|
|
|
|
//Added by MC:
|
|
bglobal.getspawned = Args.GatherFiles ("-bots", "", false);
|
|
if (bglobal.getspawned->NumArgs() == 0)
|
|
{
|
|
delete bglobal.getspawned;
|
|
bglobal.getspawned = NULL;
|
|
}
|
|
else
|
|
{
|
|
bglobal.spawn_tries = 0;
|
|
bglobal.wanted_botnum = bglobal.getspawned->NumArgs();
|
|
}
|
|
|
|
flags = dmflags;
|
|
|
|
if (Args.CheckParm ("-nomonsters")) flags |= DF_NO_MONSTERS;
|
|
if (Args.CheckParm ("-respawn")) flags |= DF_MONSTERS_RESPAWN;
|
|
if (Args.CheckParm ("-fast")) flags |= DF_FAST_MONSTERS;
|
|
|
|
devparm = Args.CheckParm ("-devparm");
|
|
|
|
if (Args.CheckParm ("-altdeath"))
|
|
{
|
|
deathmatch = 1;
|
|
flags |= DF_ITEMS_RESPAWN;
|
|
}
|
|
else if (Args.CheckParm ("-deathmatch"))
|
|
{
|
|
deathmatch = 1;
|
|
flags |= DF_WEAPONS_STAY | DF_ITEMS_RESPAWN;
|
|
}
|
|
|
|
dmflags = flags;
|
|
|
|
// get skill / episode / map from parms
|
|
if (gameinfo.gametype != GAME_Hexen)
|
|
{
|
|
strcpy (startmap, (gameinfo.flags & GI_MAPxx) ? "MAP01" : "E1M1");
|
|
}
|
|
else
|
|
{
|
|
strcpy (startmap, "&wt@01");
|
|
}
|
|
autostart = false;
|
|
|
|
const char *val = Args.CheckValue ("-skill");
|
|
if (val)
|
|
{
|
|
gameskill = val[0] - '1';
|
|
autostart = true;
|
|
}
|
|
|
|
p = Args.CheckParm ("-warp");
|
|
if (p && p < Args.NumArgs() - 1)
|
|
{
|
|
int ep, map;
|
|
|
|
if (gameinfo.flags & GI_MAPxx)
|
|
{
|
|
ep = 1;
|
|
map = atoi (Args.GetArg(p+1));
|
|
}
|
|
else
|
|
{
|
|
ep = Args.GetArg(p+1)[0]-'0';
|
|
map = p < Args.NumArgs() - 2 ? Args.GetArg(p+2)[0]-'0' : 10;
|
|
if ((unsigned)map > 9)
|
|
{
|
|
map = ep;
|
|
ep = 1;
|
|
}
|
|
}
|
|
|
|
strncpy (startmap, CalcMapName (ep, map), 8);
|
|
autostart = true;
|
|
}
|
|
|
|
//I_Error ("Oh gnos!");
|
|
// [RH] Hack to handle +map
|
|
p = Args.CheckParm ("+map");
|
|
if (p && p < Args.NumArgs()-1)
|
|
{
|
|
if (Wads.CheckNumForName (Args.GetArg (p+1)) == -1)
|
|
{
|
|
Printf ("Can't find map %s\n", Args.GetArg (p+1));
|
|
}
|
|
else
|
|
{
|
|
strncpy (startmap, Args.GetArg (p+1), 8);
|
|
Args.GetArg (p)[0] = '-';
|
|
autostart = true;
|
|
}
|
|
}
|
|
if (devparm)
|
|
{
|
|
Printf (GStrings("D_DEVSTR"));
|
|
}
|
|
|
|
#ifndef unix
|
|
// We do not need to support -cdrom under Unix, because all the files
|
|
// that would go to c:\\zdoomdat are already stored in .zdoom inside
|
|
// the user's home directory.
|
|
if (Args.CheckParm("-cdrom"))
|
|
{
|
|
Printf (GStrings("D_CDROM"));
|
|
mkdir ("c:\\zdoomdat", 0);
|
|
}
|
|
#endif
|
|
|
|
// turbo option // [RH] (now a cvar)
|
|
{
|
|
UCVarValue value;
|
|
|
|
value.String = Args.CheckValue ("-turbo");
|
|
if (value.String == NULL)
|
|
value.String = "100";
|
|
else
|
|
Printf ("turbo scale: %s%%\n", value.String);
|
|
|
|
turbo.SetGenericRepDefault (value, CVAR_String);
|
|
}
|
|
|
|
v = Args.CheckValue ("-timer");
|
|
if (v)
|
|
{
|
|
double time = strtod (v, NULL);
|
|
Printf ("Levels will end after %g minute%s.\n", time, time > 1 ? "s" : "");
|
|
timelimit = (float)time;
|
|
}
|
|
|
|
v = Args.CheckValue ("-avg");
|
|
if (v)
|
|
{
|
|
Printf ("Austin Virtual Gaming: Levels will end after 20 minutes\n");
|
|
timelimit = 20.f;
|
|
}
|
|
|
|
Printf ("Init miscellaneous info.\n");
|
|
M_Init ();
|
|
|
|
Printf ("Init Playloop state.\n");
|
|
P_Init ();
|
|
|
|
Printf ("Setting up sound.\n");
|
|
S_Init ();
|
|
|
|
Printf ("Checking network game status.\n");
|
|
D_CheckNetGame ();
|
|
|
|
// [RH] Lock any cvars that should be locked now that we're
|
|
// about to begin the game.
|
|
FBaseCVar::EnableNoSet ();
|
|
|
|
// [RH] Print an informative message if -heapsize is used
|
|
if (Args.CheckParm ("-heapsize"))
|
|
{
|
|
Printf (TEXTCOLOR_ORANGE "Starting with -heapsize is unnecessary.\n"
|
|
TEXTCOLOR_ORANGE "The zone heap is not used anymore.\n");
|
|
}
|
|
|
|
// [RH] Run any saved commands from the command line or autoexec.cfg now.
|
|
gamestate = GS_FULLCONSOLE;
|
|
Net_NewMakeTic ();
|
|
DObject::BeginFrame ();
|
|
DThinker::RunThinkers ();
|
|
DObject::EndFrame ();
|
|
gamestate = GS_STARTUP;
|
|
|
|
// start the apropriate game based on parms
|
|
v = Args.CheckValue ("-record");
|
|
|
|
if (v)
|
|
{
|
|
G_RecordDemo (v);
|
|
autostart = true;
|
|
}
|
|
|
|
files = Args.GatherFiles ("-playdemo", ".lmp", false);
|
|
if (files->NumArgs() > 0)
|
|
{
|
|
singledemo = true; // quit after one demo
|
|
G_DeferedPlayDemo (files->GetArg (0));
|
|
delete files;
|
|
D_DoomLoop (); // never returns
|
|
}
|
|
delete files;
|
|
|
|
v = Args.CheckValue ("-timedemo");
|
|
if (v)
|
|
{
|
|
G_TimeDemo (v);
|
|
D_DoomLoop (); // never returns
|
|
}
|
|
|
|
v = Args.CheckValue ("-loadgame");
|
|
if (v)
|
|
{
|
|
strncpy (file, v, sizeof(file)-1);
|
|
FixPathSeperator (file);
|
|
DefaultExtension (file, ".zds");
|
|
G_LoadGame (file);
|
|
}
|
|
|
|
if (gameaction != ga_loadgame)
|
|
{
|
|
BorderNeedRefresh = screen->GetPageCount ();
|
|
if (autostart || netgame)
|
|
{
|
|
CheckWarpTransMap (startmap, true);
|
|
G_InitNew (startmap, false);
|
|
}
|
|
else
|
|
{
|
|
D_StartTitle (); // start up intro loop
|
|
}
|
|
}
|
|
|
|
if (demorecording)
|
|
G_BeginRecording ();
|
|
|
|
atterm (D_QuitNetGame); // killough
|
|
|
|
D_DoomLoop (); // never returns
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// STAT fps
|
|
//
|
|
// Displays statistics about rendering times
|
|
//
|
|
//==========================================================================
|
|
|
|
ADD_STAT (fps, out)
|
|
{
|
|
sprintf (out,
|
|
"frame=%04.1f ms walls=%04.1f ms planes=%04.1f ms masked=%04.1f ms",
|
|
(double)FrameCycles * SecondsPerCycle * 1000,
|
|
(double)WallCycles * SecondsPerCycle * 1000,
|
|
(double)PlaneCycles * SecondsPerCycle * 1000,
|
|
(double)MaskedCycles * SecondsPerCycle * 1000
|
|
);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// STAT wallcycles
|
|
//
|
|
// Displays the minimum number of cycles spent drawing walls
|
|
//
|
|
//==========================================================================
|
|
|
|
static cycle_t bestwallcycles = INT_MAX;
|
|
|
|
ADD_STAT (wallcycles, out)
|
|
{
|
|
if (WallCycles && WallCycles < bestwallcycles)
|
|
bestwallcycles = WallCycles;
|
|
sprintf (out, "%lu", bestwallcycles);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD clearwallcycles
|
|
//
|
|
// Resets the count of minimum wall drawing cycles
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (clearwallcycles)
|
|
{
|
|
bestwallcycles = INT_MAX;
|
|
}
|
|
|
|
#if 1
|
|
// To use these, also uncomment the clock/unclock in wallscan
|
|
static cycle_t bestscancycles = INT_MAX;
|
|
|
|
ADD_STAT (scancycles, out)
|
|
{
|
|
if (WallScanCycles && WallScanCycles < bestscancycles)
|
|
bestscancycles = WallScanCycles;
|
|
sprintf (out, "%lu", bestscancycles);
|
|
}
|
|
|
|
CCMD (clearscancycles)
|
|
{
|
|
bestscancycles = INT_MAX;
|
|
}
|
|
#endif
|