ref: 3191e80bf7fda36bf0c951b0363cd52fa6ceac6b
dir: /d_main.c/
// D_main.c // HEADER FILES ------------------------------------------------------------ #include "h2stdinc.h" #if defined(__WATCOMC__) && defined(_DOS) #include <dos.h> #include <graph.h> #include <direct.h> #endif #include "doomdef.h" #include "p_local.h" #include "soundst.h" #include "v_compat.h" #include "i_system.h" // MACROS ------------------------------------------------------------------ #define MAXWADFILES 20 #define SHAREWAREWADNAME "heretic1.wad" #ifdef RENDER3D #define V_DrawPatch(x,y,p) OGL_DrawPatch((x),(y),(p)) #define V_DrawRawScreen(a) OGL_DrawRawScreen((a)) #endif // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- void D_CheckNetGame(void); void G_BuildTiccmd(ticcmd_t *cmd); void F_Drawer(void); boolean F_Responder(event_t *ev); void R_ExecuteSetViewSize(void); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- void D_ProcessEvents(void); void D_DoAdvanceDemo(void); void D_AdvanceDemo (void); void D_PageDrawer (void); // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern boolean MenuActive; extern boolean askforquit; // PUBLIC DATA DEFINITIONS ------------------------------------------------- char *basePath = DUMMY_BASEPATH; 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 singletics; // debug flag to cancel adaptiveness boolean noartiskip; // whether shift-enter skips an artifact skill_t startskill; int startepisode; int startmap; boolean autostart; boolean advancedemo; FILE *debugfile; event_t events[MAXEVENTS]; int eventhead; int eventtail; // PRIVATE DATA DEFINITIONS ------------------------------------------------ static int demosequence; static int pagetic; static const char *pagename; static char basedefault[1024]; static const char *wadfiles[MAXWADFILES + 1] = { "heretic.wad", NULL /* the last entry MUST be NULL */ }; // CODE -------------------------------------------------------------------- #if !(defined(__WATCOMC__) || defined(__DJGPP__) || defined(_WIN32)) char *strlwr (char *str) { char *c; c = str; while (*c) { *c = tolower(*c); c++; } return str; } char *strupr (char *str) { char *c; c = str; while (*c) { *c = toupper(*c); c++; } return str; } int filelength(int handle) { Dir *d; int length; d = dirfstat(handle); if (d == nil) { I_Error("Error fstating"); } length = d->length; free(d); return length; } #endif //========================================================================== // // Fixed Point math // //========================================================================== #if defined(_HAVE_FIXED_ASM) #if defined(__i386__) || defined(__386__) || defined(_M_IX86) #if defined(__GNUC__) && !defined(_INLINE_FIXED_ASM) fixed_t FixedMul (fixed_t a, fixed_t b) { fixed_t retval; __asm__ __volatile__( "imull %%edx \n\t" "shrdl $16, %%edx, %%eax \n\t" : "=a" (retval) : "a" (a), "d" (b) : "cc" ); return retval; } fixed_t FixedDiv2 (fixed_t a, fixed_t b) { fixed_t retval; __asm__ __volatile__( "cdq \n\t" "shldl $16, %%eax, %%edx \n\t" "sall $16, %%eax \n\t" "idivl %%ebx \n\t" : "=a" (retval) : "a" (a), "b" (b) : "%edx", "cc" ); return retval; } #endif /* GCC and !_INLINE_FIXED_ASM */ #endif /* x86 */ #else /* C-only versions */ fixed_t FixedMul (fixed_t a, fixed_t b) { return ((int64_t) a * (int64_t) b) >> 16; } fixed_t FixedDiv2 (fixed_t a, fixed_t b) { if (!b) return 0; return (fixed_t) (((double) a / (double) b) * FRACUNIT); } #endif fixed_t FixedDiv (fixed_t a, fixed_t b) { if ((abs(a) >> 14) >= abs(b)) { return ((a^b) < 0 ? H2MININT : H2MAXINT); } return (FixedDiv2(a, b)); } //========================================================================== // // Byte swap functions // //========================================================================== int16_t ShortSwap (int16_t x) { return (int16_t) (((uint16_t)x << 8) | ((uint16_t)x >> 8)); } int32_t LongSwap (int32_t x) { return (int32_t) (((uint32_t)x << 24) | ((uint32_t)x >> 24) | (((uint32_t)x & (uint32_t)0x0000ff00UL) << 8) | (((uint32_t)x & (uint32_t)0x00ff0000UL) >> 8)); } /* =============================================================================== EVENT HANDLING Events are asyncronous inputs generally generated by the game user. Events can be discarded if no responder claims them =============================================================================== */ //--------------------------------------------------------------------------- // // PROC D_PostEvent // // Called by the I/O functions when input is detected. // //--------------------------------------------------------------------------- QLock eventlock; // // D_PostEvent // Called by the I/O functions when input is detected // void D_PostEvent (event_t* ev) { int next; retry: qlock(&eventlock); next = (eventhead+1)&(MAXEVENTS-1); if(next == eventtail){ qunlock(&eventlock); if(ev->type != ev_keydown && ev->type != ev_keyup) return; sleep(1); goto retry; } events[eventhead] = *ev; eventhead = next; qunlock(&eventlock); } //--------------------------------------------------------------------------- // // PROC D_ProcessEvents // // Send all the events of the given timestamp down the responder chain. // //--------------------------------------------------------------------------- void D_ProcessEvents(void) { event_t *ev; while (eventtail != eventhead) { ev = &events[eventtail]; if (F_Responder(ev)) { goto _next_ev; } if (MN_Responder(ev)) { goto _next_ev; } G_Responder(ev); _next_ev: eventtail = (eventtail + 1) & (MAXEVENTS - 1); } } //--------------------------------------------------------------------------- // // 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 D_Display(void) { // Change the view size if needed if (setsizeneeded) { R_ExecuteSetViewSize(); } // // do buffered drawing // switch (gamestate) { case GS_LEVEL: if (!gametic) break; #if AM_TRANSPARENT R_RenderPlayerView (&players[displayplayer]); #endif if (automapactive) AM_Drawer (); #if !AM_TRANSPARENT else { R_RenderPlayerView (&players[displayplayer]); } #endif 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, (PATCH_REF)WR_CacheLumpName("PAUSED", PU_CACHE)); } else { V_DrawPatch(160, 70, (PATCH_REF)WR_CacheLumpName("PAUSED", PU_CACHE)); } } #ifdef RENDER3D if (OGL_DrawFilter()) BorderNeedRefresh = true; #endif // 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]; snprintf(filename, sizeof(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 =============================================================================== */ /* ================ = = 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((BYTE_REF)WR_CacheLumpName(pagename, PU_CACHE)); if (demosequence == 1) { V_DrawPatch(4, 160, (PATCH_REF)WR_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; p = M_CheckParm ("-recordfrom"); if (!p || p >= myargc - 2) return; G_LoadGame(atoi(myargv[p + 1])); G_DoLoadGame(); // load the gameskill etc info from savegame G_RecordDemo(gameskill, 1, gameepisode, gamemap, myargv[p + 2]); D_DoomLoop(); // never returns } /* =============== = = D_AddFile = =============== */ void D_AddFile(const char *file) { int i = 0; char *newwad; while (wadfiles[i]) { if (++i == MAXWADFILES) I_Error ("MAXWADFILES reached for %s", file); } newwad = (char *) malloc(strlen(file) + 1); strcpy(newwad, file); wadfiles[i] = newwad; if (++i <= MAXWADFILES) wadfiles[i] = NULL; } //========================================================== // // Startup Thermo code // FIXME : MOVE OR REMOVE THIS... //========================================================== #if defined(__WATCOMC__) && defined(_DOS) #define MSG_Y 9 /* #define THERM_X 15 #define THERM_Y 16 #define THERMCOLOR 3 */ #define THERM_X 14 #define THERM_Y 14 static int thermMax; static int thermCurrent; // // Heretic startup screen shit // static byte *hscreen; static char *startup; // * to text screen static char smsg[80]; // status bar line static char tmsg[300]; static void hgotoxy(int x,int y) { hscreen = (byte *)(0xb8000 + y*160 + x*2); } static void hput(unsigned char c, unsigned char a) { *hscreen++ = c; *hscreen++ = a; } static void hprintf(const char *string, unsigned char a) { int i; if (debugmode) { puts(string); return; } for (i = 0; i < strlen(string); i++) { hput(string[i], a); } } static void drawstatus(void) { if(debugmode) { return; } _settextposition(25, 2); _setbkcolor(1); _settextcolor(15); _outtext(smsg); _settextposition(25, 1); } static void status(const char *string) { strcat(smsg, string); drawstatus(); } static void DrawThermo(void) { 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 } static 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); } void tprintf(const char *msg, int initflag) { # if 0 char temp[80]; int i, start, add; if (debugmode) { printf(msg); return; } 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(); # endif } void CheckAbortStartup(void) { #if defined(__WATCOMC__) && defined(_DOS) extern int lastpress; if (lastpress == 1) { // Abort if escape pressed union REGS regs; I_ShutdownKeyboard(); regs.x.eax = 0x3; int386(0x10, ®s, ®s); printf("Exited from HERETIC.\n"); exit(1); } #endif } void IncThermo(void) { thermCurrent++; DrawThermo(); CheckAbortStartup(); } void InitThermo(int max) { thermMax = max; thermCurrent = 0; } #else #define hgotoxy(x,y) do {} while (0) #define hprintf(s,a) puts((s)) #define status(s) puts((s)) #define DrawThermo() do {} while (0) void tprintf(const char *msg, int initflag) { # if 0 printf(msg); # endif } void CheckAbortStartup(void) {} void IncThermo(void) {} void InitThermo(int x) {} #endif //--------------------------------------------------------------------------- // // PROC D_DoomMain // //--------------------------------------------------------------------------- void D_DoomMain(void) { int p, e, m, i; char file[MAX_OSPATH]; boolean devMap; char *slash; char *wadloc[nelem(wadfiles)]; 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; // -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]; snprintf(file, sizeof(file), "%se%cm%c.wad", DEVMAPDIR, 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) { snprintf(file, sizeof(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(); printf("Z_Init: Init zone memory allocation daemon.\n"); Z_Init(); printf("W_Init: Init WADfiles.\n"); I_SetupPath(wadfiles); 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; } // Load defaults before initing other systems printf("M_LoadDefaults: Load system defaults.\n"); M_LoadDefaults("cfg"); #if defined(__WATCOMC__) && defined(_DOS) I_StartupKeyboard(); I_StartupJoystick(); startup = (char *) W_CacheLumpName("LOADING", PU_CACHE); blitStartup(); // Build status bar line! smsg[0] = 0; #endif if (deathmatch) status("DeathMatch..."); if (nomonsters) status("No Monsters..."); if (respawnparm) status("Respawning..."); if (autostart) { char temp[64]; snprintf(temp, sizeof(temp), "Warp to Episode %d, Map %d, Skill %d ", startepisode, startmap, startskill + 1); status(temp); } 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(); #if defined(__WATCOMC__) && defined(_DOS) 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) { G_LoadGame(atoi(myargv[p+1])); } // 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(); } } #if defined(__WATCOMC__) && defined(_DOS) _settextcursor(0x0607); // bring the cursor back #endif D_DoomLoop(); // Never returns }