Source to src/d_main.c
// D_main.c
#ifdef __WATCOMC__
#include <dos.h>
#include <graph.h>
#include <sys\types.h>
#include <direct.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include "DoomDef.h"
#include "P_local.h"
#include "soundst.h"
boolean shareware = false; // true if only episode 1 present
boolean ExtendedWAD = false; // true if episodes 4 and 5 present
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;
boolean autostart;
extern boolean automapactive;
boolean advancedemo;
FILE *debugfile;
void D_CheckNetGame(void);
void D_ProcessEvents(void);
void G_BuildTiccmd(ticcmd_t *cmd);
void D_DoAdvanceDemo(void);
void D_PageDrawer (void);
void D_AdvanceDemo (void);
void F_Drawer(void);
boolean F_Responder(event_t *ev);
//---------------------------------------------------------------------------
//
// FUNC FixedDiv
//
//---------------------------------------------------------------------------
fixed_t FixedDiv(fixed_t a, fixed_t b)
{
if((abs(a)>>14) >= abs(b))
{
return((a^b)<0 ? MININT : MAXINT);
}
return(FixedDiv2(a, b));
}
/*
===============================================================================
EVENT HANDLING
Events are asyncronous inputs generally generated by the game user.
Events can be discarded if no responder claims them
===============================================================================
*/
event_t events[MAXEVENTS];
int eventhead;
int eventtail;
//---------------------------------------------------------------------------
//
// PROC D_PostEvent
//
// Called by the I/O functions when input is detected.
//
//---------------------------------------------------------------------------
void D_PostEvent(event_t *ev)
{
events[eventhead] = *ev;
eventhead = (++eventhead)&(MAXEVENTS-1);
}
//---------------------------------------------------------------------------
//
// PROC D_ProcessEvents
//
// Send all the events of the given timestamp down the responder chain.
//
//---------------------------------------------------------------------------
void D_ProcessEvents(void)
{
event_t *ev;
for(; eventtail != eventhead; eventtail = (++eventtail)&(MAXEVENTS-1))
{
ev = &events[eventtail];
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 MenuActive;
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(paused && !MenuActive && !askforquit)
{
if(!netgame)
{
V_DrawPatch(160, viewwindowy+5, W_CacheLumpName("PAUSED",
PU_CACHE));
}
else
{
V_DrawPatch(160, 70, W_CacheLumpName("PAUSED",
PU_CACHE));
}
}
// Handle player messages
DrawMessage();
// Menu drawing
MN_Drawer();
// Send out any new accumulation
NetUpdate();
// Flush buffered stuff to screen
I_Update();
}
//---------------------------------------------------------------------------
//
// PROC D_DoomLoop
//
//---------------------------------------------------------------------------
void D_DoomLoop(void)
{
if(M_CheckParm("-debugfile"))
{
char filename[20];
sprintf(filename, "debug%i.txt", consoleplayer);
debugfile = fopen(filename,"w");
}
I_InitGraphics();
while(1)
{
// Frame syncronous IO operations
I_StartFrame();
// Process one or more tics
if(singletics)
{
I_StartTic();
D_ProcessEvents();
G_BuildTiccmd(&netcmds[consoleplayer][maketic%BACKUPTICS]);
if (advancedemo)
D_DoAdvanceDemo ();
G_Ticker();
gametic++;
maketic++;
}
else
{
// 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
=
================
*/
extern boolean MenuActive;
void D_PageDrawer(void)
{
V_DrawRawScreen(W_CacheLumpName(pagename, PU_CACHE));
if(demosequence == 1)
{
V_DrawPatch(4, 160, W_CacheLumpName("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 = "TITLE";
S_StartSong(mus_titl, false);
break;
case 1:
pagetic = 140;
gamestate = GS_DEMOSCREEN;
pagename = "TITLE";
break;
case 2:
BorderNeedRefresh = true;
UpdateState |= I_FULLSCRN;
G_DeferedPlayDemo ("demo1");
break;
case 3:
pagetic = 200;
gamestate = GS_DEMOSCREEN;
pagename = "CREDIT";
break;
case 4:
BorderNeedRefresh = true;
UpdateState |= I_FULLSCRN;
G_DeferedPlayDemo ("demo2");
break;
case 5:
pagetic = 200;
gamestate = GS_DEMOSCREEN;
if(shareware)
{
pagename = "ORDER";
}
else
{
pagename = "CREDIT";
}
break;
case 6:
BorderNeedRefresh = true;
UpdateState |= I_FULLSCRN;
G_DeferedPlayDemo ("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 file[256];
p = M_CheckParm ("-recordfrom");
if (!p || p > myargc-2)
return;
if(cdrom)
{
sprintf(file, SAVEGAMENAMECD"%c.hsg",myargv[p+1][0]);
}
else
{
sprintf(file, SAVEGAMENAME"%c.hsg",myargv[p+1][0]);
}
G_LoadGame (file);
G_DoLoadGame (); // load the gameskill etc info from savegame
G_RecordDemo (gameskill, 1, gameepisode, gamemap, myargv[p+2]);
D_DoomLoop (); // never returns
}
/*
===============
=
= D_AddFile
=
===============
*/
#define MAXWADFILES 20
// MAPDIR should be defined as the directory that holds development maps
// for the -wart # # command
#ifdef __NeXT__
#define MAPDIR "/Novell/Heretic/data/"
#define SHAREWAREWADNAME "/Novell/Heretic/source/heretic1.wad"
char *wadfiles[MAXWADFILES] =
{
"/Novell/Heretic/source/heretic.wad",
"/Novell/Heretic/data/texture1.lmp",
"/Novell/Heretic/data/texture2.lmp",
"/Novell/Heretic/data/pnames.lmp"
};
#else
#define MAPDIR "\\data\\"
#define SHAREWAREWADNAME "heretic1.wad"
char *wadfiles[MAXWADFILES] =
{
"heretic.wad",
"texture1.lmp",
"texture2.lmp",
"pnames.lmp"
};
#endif
char *basedefault = "heretic.cfg";
char exrnwads[80];
char exrnwads2[80];
void wadprintf(void)
{
if(debugmode)
{
return;
}
#ifdef __WATCOMC__
_settextposition(23, 2);
_setbkcolor(1);
_settextcolor(0);
_outtext(exrnwads);
_settextposition(24, 2);
_outtext(exrnwads2);
#endif
}
void D_AddFile(char *file)
{
int numwadfiles;
char *new;
// char text[256];
for(numwadfiles = 0; wadfiles[numwadfiles]; numwadfiles++);
new = malloc(strlen(file)+1);
strcpy(new, file);
if(strlen(exrnwads)+strlen(file) < 78)
{
if(strlen(exrnwads))
{
strcat(exrnwads, ", ");
}
else
{
strcpy(exrnwads, "External Wadfiles: ");
}
strcat(exrnwads, file);
}
else if(strlen(exrnwads2)+strlen(file) < 79)
{
if(strlen(exrnwads2))
{
strcat(exrnwads2, ", ");
}
else
{
strcpy(exrnwads2, " ");
strcat(exrnwads, ",");
}
strcat(exrnwads2, file);
}
wadfiles[numwadfiles] = new;
}
//==========================================================
//
// Startup Thermo code
//
//==========================================================
#define MSG_Y 9
//#define THERM_X 15
//#define THERM_Y 16
//#define THERMCOLOR 3
#define THERM_X 14
#define THERM_Y 14
int thermMax;
int thermCurrent;
char *startup; // * to text screen
char smsg[80]; // status bar line
//
// Heretic startup screen shit
//
byte *hscreen;
void hgotoxy(int x,int y)
{
hscreen = (byte *)(0xb8000 + y*160 + x*2);
}
void hput(unsigned char c, unsigned char a)
{
*hscreen++ = c;
*hscreen++ = a;
}
void hprintf(char *string, unsigned char a)
{
#ifdef __WATCOMC__
int i;
if(debugmode)
{
puts(string);
return;
}
for(i = 0; i < strlen(string); i++)
{
hput(string[i], a);
}
#endif
}
void drawstatus(void)
{
if(debugmode)
{
return;
}
#ifdef __WATCOMC__
_settextposition(25, 2);
_setbkcolor(1);
_settextcolor(15);
_outtext(smsg);
_settextposition(25, 1);
#endif
}
void status(char *string)
{
strcat(smsg,string);
drawstatus();
}
void DrawThermo(void)
{
#ifdef __WATCOMC__
unsigned char *screen;
int progress;
int i;
if(debugmode)
{
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
progress = (50*thermCurrent)/thermMax+2;
// screen = (char *)0xb8000 + (THERM_Y*160 + THERM_X*2);
hgotoxy(THERM_X,THERM_Y);
for (i = 0; i < progress; i++)
{
// *screen++ = 0xdb;
// *screen++ = 0x2a;
hput(0xdb,0x2a);
}
#endif
#endif
}
#ifdef __WATCOMC__
void blitStartup(void)
{
byte *textScreen;
if(debugmode)
{
return;
}
// Blit main screen
textScreen = (byte *)0xb8000;
memcpy(textScreen, startup, 4000);
// Print version string
_setbkcolor(4); // Red
_settextcolor(14); // Yellow
_settextposition(3, 47);
_outtext(VERSION_TEXT);
// Hide cursor
_settextcursor(0x2000);
}
#endif
char tmsg[300];
void tprintf(char *msg,int initflag)
{
#if 0
#ifdef __WATCOMC__
char temp[80];
int start;
int add;
int i;
#endif
if(debugmode)
{
printf(msg);
return;
}
#ifdef __WATCOMC__
if (initflag)
tmsg[0] = 0;
strcat(tmsg,msg);
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);
strncpy(temp,tmsg+start,i-start);
_settextposition(MSG_Y+add,40-strlen(temp)/2);
_outtext(temp);
start = i+1;
add++;
}
_settextposition(25,1);
drawstatus();
#else
printf(msg);
#endif
#endif
}
void CheckAbortStartup(void)
{
#ifdef __WATCOMC__
extern int lastpress;
if(lastpress == 1)
{ // Abort if escape pressed
CleanExit();
}
#endif
}
void IncThermo(void)
{
thermCurrent++;
DrawThermo();
CheckAbortStartup();
}
void InitThermo(int max)
{
thermMax = max;
thermCurrent = 0;
}
#ifdef __WATCOMC__
void CleanExit(void)
{
union REGS regs;
I_ShutdownKeyboard();
regs.x.eax = 0x3;
int386(0x10, ®s, ®s);
printf("Exited from HERETIC.\n");
exit(1);
}
#endif
//---------------------------------------------------------------------------
//
// PROC D_DoomMain
//
//---------------------------------------------------------------------------
void D_DoomMain(void)
{
int p;
int e;
int m;
char file[256];
FILE *fp;
boolean devMap;
//char *screen;
M_FindResponseFile();
setbuf(stdout, NULL);
nomonsters = M_CheckParm("-nomonsters");
respawnparm = M_CheckParm("-respawn");
ravpic = M_CheckParm("-ravpic");
noartiskip = M_CheckParm("-noartiskip");
debugmode = M_CheckParm("-debug");
startskill = sk_medium;
startepisode = 1;
startmap = 1;
autostart = false;
// wadfiles[0] is a char * to the main wad
fp = fopen(wadfiles[0], "rb");
if(fp)
{
fclose(fp);
}
else
{ // Change to look for shareware wad
wadfiles[0] = SHAREWAREWADNAME;
}
// Check for -CDROM
cdrom = false;
#ifdef __WATCOMC__
if(M_CheckParm("-cdrom"))
{
cdrom = true;
mkdir("c:\\heretic.cd");
}
#endif
// -FILE [filename] [filename] ...
// Add files to the wad list.
p = M_CheckParm("-file");
if(p)
{ // the parms after p are wadfile/lump names, until end of parms
// or another - preceded parm
while(++p != myargc && myargv[p][0] != '-')
{
D_AddFile(myargv[p]);
}
}
// -DEVMAP <episode> <map>
// Adds a map wad from the development directory to the wad list,
// and sets the start episode and the start map.
devMap = false;
p = M_CheckParm("-devmap");
if(p && p < myargc-2)
{
e = myargv[p+1][0];
m = myargv[p+2][0];
sprintf(file, MAPDIR"E%cM%c.wad", e, m);
D_AddFile(file);
printf("DEVMAP: Episode %c, Map %c.\n", e, m);
startepisode = e-'0';
startmap = m-'0';
autostart = true;
devMap = true;
}
p = M_CheckParm("-playdemo");
if(!p)
{
p = M_CheckParm("-timedemo");
}
if (p && p < myargc-1)
{
sprintf(file, "%s.lmp", myargv[p+1]);
D_AddFile(file);
printf("Playing demo %s.lmp.\n", myargv[p+1]);
}
//
// get skill / episode / map from parms
//
if(M_CheckParm("-deathmatch"))
{
deathmatch = true;
}
p = M_CheckParm("-skill");
if(p && p < myargc-1)
{
startskill = myargv[p+1][0]-'1';
autostart = true;
}
p = M_CheckParm("-episode");
if(p && p < myargc-1)
{
startepisode = myargv[p+1][0]-'0';
startmap = 1;
autostart = true;
}
p = M_CheckParm("-warp");
if(p && p < myargc-2)
{
startepisode = myargv[p+1][0]-'0';
startmap = myargv[p+2][0]-'0';
autostart = true;
}
//
// init subsystems
//
printf("V_Init: allocate screens.\n");
V_Init();
// Load defaults before initing other systems
printf("M_LoadDefaults: Load system defaults.\n");
M_LoadDefaults();
printf("Z_Init: Init zone memory allocation daemon.\n");
Z_Init();
printf("W_Init: Init WADfiles.\n");
W_InitMultipleFiles(wadfiles);
if(W_CheckNumForName("E2M1") == -1)
{ // Can't find episode 2 maps, must be the shareware WAD
shareware = true;
}
else if(W_CheckNumForName("EXTENDED") != -1)
{ // Found extended lump, must be the extended WAD
ExtendedWAD = true;
}
#ifdef __WATCOMC__
I_StartupKeyboard();
I_StartupJoystick();
#endif
startup = W_CacheLumpName("LOADING", PU_CACHE);
#ifdef __WATCOMC__
blitStartup();
#endif
//
// Build status bar line!
//
smsg[0] = 0;
if (deathmatch)
status("DeathMatch...");
if (nomonsters)
status("No Monsters...");
if (respawnparm)
status("Respawning...");
if (autostart)
{
char temp[64];
sprintf(temp, "Warp to Episode %d, Map %d, Skill %d ",
startepisode, startmap, startskill+1);
status(temp);
}
wadprintf(); // print the added wadfiles
tprintf("MN_Init: Init menu system.\n",1);
MN_Init();
CT_Init();
tprintf("R_Init: Init Heretic refresh daemon.",1);
hgotoxy(17,7);
hprintf("Loading graphics",0x3f);
R_Init();
tprintf("P_Init: Init Playloop state.",1);
hgotoxy(17,8);
hprintf("Init game engine.",0x3f);
P_Init();
IncThermo();
tprintf("I_Init: Setting up machine state.\n",1);
I_Init();
IncThermo();
tprintf("D_CheckNetGame: Checking network game status.\n",1);
hgotoxy(17,9);
hprintf("Checking network game status.", 0x3f);
D_CheckNetGame();
IncThermo();
#ifdef __WATCOMC__
I_CheckExternDriver(); // Check for an external device driver
#endif
tprintf("SB_Init: Loading patches.\n",1);
SB_Init();
IncThermo();
//
// start the apropriate game based on parms
//
D_CheckRecordFrom();
p = M_CheckParm("-record");
if(p && p < myargc-1)
{
G_RecordDemo(startskill, 1, startepisode, startmap, myargv[p+1]);
D_DoomLoop(); // Never returns
}
p = M_CheckParm("-playdemo");
if(p && p < myargc-1)
{
singledemo = true; // Quit after one demo
G_DeferedPlayDemo(myargv[p+1]);
D_DoomLoop(); // Never returns
}
p = M_CheckParm("-timedemo");
if(p && p < myargc-1)
{
G_TimeDemo(myargv[p+1]);
D_DoomLoop(); // Never returns
}
p = M_CheckParm("-loadgame");
if(p && p < myargc-1)
{
if(cdrom)
{
sprintf(file, SAVEGAMENAMECD"%c.hsg", myargv[p+1][0]);
}
else
{
sprintf(file, SAVEGAMENAME"%c.hsg", myargv[p+1][0]);
}
G_LoadGame(file);
}
// Check valid episode and map
if((autostart || netgame) && (devMap == false))
{
if(M_ValidEpisodeMap(startepisode, startmap) == false)
{
startepisode = 1;
startmap = 1;
}
}
if(gameaction != ga_loadgame)
{
UpdateState |= I_FULLSCRN;
BorderNeedRefresh = true;
if(autostart || netgame)
{
G_InitNew(startskill, startepisode, startmap);
}
else
{
D_StartTitle();
}
}
#ifdef __WATCOMC__
_settextcursor(0x0607); // bring the cursor back
#endif
D_DoomLoop(); // Never returns
}