ref: fc6385246c666b4e9a58ce17cf68a76d06bf03a2
dir: /src/strife/d_main.c/
// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA // 02111-1307, USA. // // 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. // //----------------------------------------------------------------------------- #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "config.h" #include "deh_main.h" #include "doomdef.h" #include "doomstat.h" #include "dstrings.h" #include "doomfeatures.h" #include "sounds.h" #include "d_iwad.h" #include "z_zone.h" #include "w_main.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_config.h" #include "m_controls.h" #include "m_misc.h" #include "m_menu.h" #include "p_saveg.h" #include "i_endoom.h" #include "i_joystick.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "i_swap.h" #include "g_game.h" #include "hu_stuff.h" #include "wi_stuff.h" #include "st_stuff.h" #include "am_map.h" #include "net_client.h" #include "net_dedicated.h" #include "net_query.h" #include "p_setup.h" #include "r_local.h" #include "d_main.h" // // D-DoomLoop() // Not a globally visible function, // just included for source reference, // called by D_DoomMain, never exits. // Manages timing and IO, // calls all ?_Responder, ?_Ticker, and ?_Drawer, // calls I_GetTime, I_StartFrame, and I_StartTic // void D_DoomLoop (void); static boolean D_AddFile(char *filename); // Location where savegames are stored char * savegamedir; // location of IWAD and WAD files char * iwadfile; boolean devparm; // started game with -devparm boolean nomonsters; // checkparm of -nomonsters boolean respawnparm; // checkparm of -respawn boolean fastparm; // checkparm of -fast boolean singletics = false; // debug flag to cancel adaptiveness //extern int soundVolume; //extern int sfxVolume; //extern int musicVolume; extern boolean inhelpscreens; skill_t startskill; int startepisode; int startmap; boolean autostart; int startloadgame; FILE* debugfile; boolean advancedemo; // villsa [STRIFE] workparm variable (similar to devparm?) boolean workparm = false; // villsa [STRIFE] stonecold cheat variable boolean stonecold = false; // haleyjd 09/11/10: [STRIFE] Game type variables boolean isregistered; boolean isdemoversion; // Store demo, do not accept any inputs // haleyjd [STRIFE] Unused. //boolean storedemo; char wadfile[1024]; // primary wad file char mapdir[1024]; // directory of development maps int show_endoom = 1; void D_CheckNetGame (void); void D_ProcessEvents (void); void G_BuildTiccmd (ticcmd_t* cmd); void D_DoAdvanceDemo (void); // // D_ProcessEvents // Send all the events of the given timestamp down the responder chain // void D_ProcessEvents (void) { event_t* ev; // haleyjd 08/22/2010: [STRIFE] there is no such thing as a "store demo" // version of Strife // IF STORE DEMO, DO NOT ACCEPT INPUT //if (storedemo) // return; while ((ev = D_PopEvent()) != NULL) { if (M_Responder (ev)) continue; // menu ate the event G_Responder (ev); } } // // D_Display // draw current display, possibly wiping it from the previous // // wipegamestate can be set to -1 to force a wipe on the next draw // // haleyjd 08/23/10: [STRIFE]: // * Changes to eliminate intermission and change timing of screenwipe // * 9/01/10: Added ST_DrawExternal and popupactivestate static variable // gamestate_t wipegamestate = GS_DEMOSCREEN; extern boolean setsizeneeded; extern int showMessages; void R_ExecuteSetViewSize (void); void D_Display (void) { static boolean viewactivestate = false; static boolean menuactivestate = false; static boolean inhelpscreensstate = false; static boolean popupactivestate = false; // [STRIFE] static boolean fullscreen = false; static gamestate_t oldgamestate = -1; static int borderdrawcount; int nowtime; int tics; int wipestart; int y; boolean done; boolean wipe; boolean redrawsbar; if (nodrawers) return; // for comparative timing / profiling redrawsbar = false; // change the view size if needed if (setsizeneeded) { R_ExecuteSetViewSize (); oldgamestate = -1; // force background redraw borderdrawcount = 3; } // save the current screen if about to wipe if (gamestate != wipegamestate) { wipe = true; wipe_StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); } else wipe = false; if (gamestate == GS_LEVEL && gametic) HU_Erase(); // do buffered drawing switch (gamestate) { case GS_LEVEL: if (!gametic) break; if (automapactive) AM_Drawer (); if (wipe || (viewheight != 200 && fullscreen) ) redrawsbar = true; // haleyjd 08/29/10: [STRIFE] Always redraw sbar if menu is/was active if (menuactivestate || (inhelpscreensstate && !inhelpscreens)) redrawsbar = true; // just put away the help screen ST_Drawer (viewheight == 200, redrawsbar ); fullscreen = viewheight == 200; break; // haleyjd 08/23/2010: [STRIFE] No intermission /* case GS_INTERMISSION: WI_Drawer (); break; */ case GS_FINALE: F_Drawer (); break; case GS_DEMOSCREEN: D_PageDrawer (); break; default: break; } // draw buffered stuff to screen I_UpdateNoBlit (); // draw the view directly if (gamestate == GS_LEVEL && !automapactive && gametic) R_RenderPlayerView (&players[displayplayer]); if (gamestate == GS_LEVEL && gametic) { HU_Drawer (); if(ST_DrawExternal()) popupactivestate = true; else if(popupactivestate) { popupactivestate = false; menuactivestate = 1; } } // clean up border stuff if (gamestate != oldgamestate && gamestate != GS_LEVEL) I_SetPalette (W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE)); // see if the border needs to be initially drawn if (gamestate == GS_LEVEL && oldgamestate != GS_LEVEL) { viewactivestate = false; // view was not active R_FillBackScreen (); // draw the pattern into the back screen } // see if the border needs to be updated to the screen if (gamestate == GS_LEVEL && !automapactive && scaledviewwidth != 320) { if (menuactive || menuactivestate || !viewactivestate) { borderdrawcount = 3; popupactivestate = false; } if (borderdrawcount) { R_DrawViewBorder (); // erase old menu stuff borderdrawcount--; } } if (testcontrols) { // Box showing current mouse speed G_DrawMouseSpeedBox(); } menuactivestate = menuactive; viewactivestate = viewactive; inhelpscreensstate = inhelpscreens; oldgamestate = wipegamestate = gamestate; // draw pause pic if (paused) { if (automapactive) y = 4; else y = viewwindowy+4; V_DrawPatchDirect(viewwindowx + (scaledviewwidth - 68) / 2, y, W_CacheLumpName (DEH_String("M_PAUSE"), PU_CACHE)); } // menus go directly to the screen M_Drawer (); // menu is drawn even on top of everything NetUpdate (); // send out any new accumulation // normal update if (!wipe) { I_FinishUpdate (); // page flip or blit buffer return; } // wipe update wipe_EndScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); wipestart = I_GetTime () - 1; do { do { nowtime = I_GetTime (); tics = nowtime - wipestart; I_Sleep(1); } while (tics < 3); // haleyjd 08/23/2010: [STRIFE] Changed from == 0 to < 3 // haleyjd 08/26/10: [STRIFE] Changed to use ColorXForm wipe. wipestart = nowtime; done = wipe_ScreenWipe(wipe_ColorXForm , 0, 0, SCREENWIDTH, SCREENHEIGHT, tics); I_UpdateNoBlit (); M_Drawer (); // menu is drawn even on top of wipes I_FinishUpdate (); // page flip or blit buffer } while (!done); } // // Add configuration file variable bindings. // void D_BindVariables(void) { int i; M_ApplyPlatformDefaults(); I_BindVideoVariables(); I_BindJoystickVariables(); I_BindSoundVariables(); M_BindBaseControls(); M_BindWeaponControls(); M_BindMapControls(); M_BindMenuControls(); M_BindStrifeControls(); // haleyjd 09/01/10: [STRIFE] M_BindChatControls(MAXPLAYERS); key_multi_msgplayer[0] = HUSTR_KEYGREEN; key_multi_msgplayer[1] = HUSTR_KEYINDIGO; key_multi_msgplayer[2] = HUSTR_KEYBROWN; key_multi_msgplayer[3] = HUSTR_KEYRED; #ifdef FEATURE_MULTIPLAYER NET_BindVariables(); #endif // haleyjd 08/29/10: [STRIFE] // * Added voice volume // * Added back flat M_BindVariable("mouse_sensitivity", &mouseSensitivity); M_BindVariable("sfx_volume", &sfxVolume); M_BindVariable("music_volume", &musicVolume); M_BindVariable("voice_volume", &voiceVolume); M_BindVariable("show_messages", &showMessages); M_BindVariable("screenblocks", &screenblocks); M_BindVariable("detaillevel", &detailLevel); M_BindVariable("snd_channels", &snd_channels); M_BindVariable("vanilla_savegame_limit", &vanilla_savegame_limit); M_BindVariable("vanilla_demo_limit", &vanilla_demo_limit); M_BindVariable("show_endoom", &show_endoom); M_BindVariable("back_flat", &back_flat); // Multiplayer chat macros for (i=0; i<10; ++i) { char buf[12]; sprintf(buf, "chatmacro%i", i); M_BindVariable(buf, &chat_macros[i]); } } // // D_GrabMouseCallback // // Called to determine whether to grab the mouse pointer // boolean D_GrabMouseCallback(void) { // Drone players don't need mouse focus if (drone) return false; // when menu is active or game is paused, release the mouse if (menuactive || paused) return false; // only grab mouse when playing levels (but not demos) return (gamestate == GS_LEVEL) && !demoplayback; } // // D_DoomLoop // // haleyjd 08/23/10: [STRIFE] Verified unmodified. // extern boolean demorecording; void D_DoomLoop (void) { if (demorecording) G_BeginRecording (); if (M_CheckParm ("-debugfile")) { char filename[20]; sprintf (filename,"debug%i.txt",consoleplayer); printf ("debug output to: %s\n",filename); debugfile = fopen (filename,"w"); } TryRunTics(); I_SetWindowTitle(gamedescription); I_InitGraphics(); I_EnableLoadingDisk(); I_SetGrabMouseCallback(D_GrabMouseCallback); V_RestoreBuffer(); R_ExecuteSetViewSize(); D_StartGameLoop(); if (testcontrols) { wipegamestate = gamestate; } 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 (); M_Ticker (); G_Ticker (); gametic++; maketic++; } else { TryRunTics (); // will run at least one tic } S_UpdateSounds (players[consoleplayer].mo);// move positional sounds // Update display, next frame, with current state. if (screenvisible) D_Display (); } } // // DEMO LOOP // int demosequence; int pagetic; char *pagename; // // D_PageTicker // Handles timing for warped projection // // haleyjd 08/22/2010: [STRIFE] verified unmodified // void D_PageTicker (void) { if (--pagetic < 0) D_AdvanceDemo (); } // // D_PageDrawer // // haleyjd 08/22/2010: [STRIFE] verified unmodified // void D_PageDrawer (void) { V_DrawPatch (0, 0, W_CacheLumpName(pagename, PU_CACHE)); } // // D_AdvanceDemo // Called after each demo or intro demosequence finishes // // haleyjd 08/22/2010: [STRIFE] verified unmodified // void D_AdvanceDemo (void) { advancedemo = true; } // // This cycles through the demo sequences. // FIXME - version dependend demo numbers? // void D_DoAdvanceDemo (void) { players[consoleplayer].playerstate = PST_LIVE; // not reborn advancedemo = false; usergame = false; // no save / end game here paused = false; gameaction = ga_nothing; // villsa 09/12/10: [STRIFE] converted pagetics to ticrate switch (demosequence) { case -5: // exit the game I_Quit(); return; case -4: // show exit screen menuactive = false; pagetic = (3*TICRATE); gamestate = GS_DEMOSCREEN; pagename = DEH_String("PANEL7"); S_StartMusic(mus_fast); if(isdemoversion) demosequence = -3; // show Velocity logo else demosequence = -5; // exit return; case -3: // show Velocity logo for demo version pagetic = (6*TICRATE); gamestate = GS_DEMOSCREEN; pagename = DEH_String("vellogo"); demosequence = -5; // exit return; case -2: // title screen pagetic = (6*TICRATE); gamestate = GS_DEMOSCREEN; pagename = DEH_String("TITLEPIC"); S_StartMusic(mus_logo); demosequence = -1; // start intro cinematic return; case -1: // start of intro cinematic pagetic = 10; gamestate = GS_DEMOSCREEN; pagename = DEH_String("PANEL0"); S_StartSound(NULL, sfx_rb2act); wipegamestate = -1; break; case 0: // Rogue logo pagetic = (4*TICRATE); gamestate = GS_DEMOSCREEN; pagename = DEH_String("RGELOGO"); wipegamestate = -1; break; case 1: pagetic = (7*TICRATE); // The comet struck our planet without gamestate = GS_DEMOSCREEN; // warning.We lost our paradise in a pagename = DEH_String("PANEL1"); // single, violent stroke. I_StartVoice(DEH_String("pro1")); S_StartMusic(mus_intro); break; case 2: pagetic = (9*TICRATE); // The impact released a virus which gamestate = GS_DEMOSCREEN; // swept through the land and killed pagename = DEH_String("PANEL2"); // millions. They turned out to be I_StartVoice(DEH_String("pro2")); // the lucky ones... break; case 3: pagetic = (12*TICRATE); // For those that did not die became gamestate = GS_DEMOSCREEN; // mutations of humanity. Some became pagename = DEH_String("PANEL3"); // fanatics who heard the voice of a I_StartVoice(DEH_String("pro3")); // malignant God in their heads, and break; // called themselves the Order. case 4: pagetic = (11*TICRATE); // Those of us who were deaf to this pagename = DEH_String("PANEL4"); // voice suffer horribly and are gamestate = GS_DEMOSCREEN; // forced to serve these ruthless I_StartVoice(DEH_String("pro4")); // psychotics, who wield weapons more break; // powerful than anything we can muster. case 5: pagetic = (10*TICRATE); // They destroy our women and children, gamestate = GS_DEMOSCREEN; // so that we must hide them underground, pagename = DEH_String("PANEL5"); // and live like animals in constant I_StartVoice(DEH_String("pro5")); // fear for our lives. break; case 6: // But there are whispers of discontent. pagetic = (16*TICRATE); // If we organize, can we defeat our gamestate = GS_DEMOSCREEN; // masters? Weapons are being stolen, pagename = DEH_String("PANEL6"); // soldiers are being trained. A I_StartVoice(DEH_String("pro6")); // Movement is born! Born of lifelong break; // STRIFE! case 7: // titlepic again - unused... pagetic = (9*TICRATE); gamestate = GS_DEMOSCREEN; pagename = DEH_String("TITLEPIC"); wipegamestate = -1; break; case 8: // demo //ClearTmp(); STRIFE-TODO pagetic = (9*TICRATE); G_DeferedPlayDemo(DEH_String("demo1")); break; case 9: // velocity logo? - unused... pagetic = (6*TICRATE); gamestate = GS_DEMOSCREEN; pagename = DEH_String("vellogo"); wipegamestate = -1; break; case 10: // credits gamestate = GS_DEMOSCREEN; pagetic = (12*TICRATE); pagename = DEH_String("CREDIT"); wipegamestate = -1; break; default: break; } ++demosequence; if(demosequence > 11) demosequence = -2; if(demosequence == 7 || demosequence == 9) ++demosequence; } // // D_StartTitle // // [STRIFE] // haleyjd 09/11/10: Small modifications for new demo sequence. // void D_StartTitle (void) { gamestate = GS_DEMOSCREEN; gameaction = ga_nothing; demosequence = -2; D_AdvanceDemo (); } // // D_QuitGame // // [STRIFE] New function // haleyjd 09/11/10: Sets up the quit game snippet powered by the // demo sequence. // void D_QuitGame(void) { gameaction = ga_nothing; demosequence = -4; D_AdvanceDemo(); } // Strings for dehacked replacements of the startup banner // // These are from the original source: some of them are perhaps // not used in any dehacked patches static char *banners[] = { // strife1.wad: " " "STRIFE: Quest for the Sigil v1.31" " " }; // // Get game name: if the startup banner has been replaced, use that. // Otherwise, use the name given // static char *GetGameName(char *gamename) { size_t i; char *deh_sub; for (i=0; i<arrlen(banners); ++i) { // Has the banner been replaced? deh_sub = DEH_String(banners[i]); if (deh_sub != banners[i]) { // Has been replaced // We need to expand via printf to include the Doom version // number // We also need to cut off spaces to get the basic name gamename = Z_Malloc(strlen(deh_sub) + 10, PU_STATIC, 0); sprintf(gamename, deh_sub, DOOM_VERSION / 100, DOOM_VERSION % 100); while (gamename[0] != '\0' && isspace(gamename[0])) strcpy(gamename, gamename+1); while (gamename[0] != '\0' && isspace(gamename[strlen(gamename)-1])) gamename[strlen(gamename) - 1] = '\0'; return gamename; } } return gamename; } // // haleyjd: STRIFE-FIXME: Temporary? // Code borrowed from Eternity, and modified to return separator char // char M_GetFilePath(const char *fn, char *dest, size_t len) { boolean found_slash = false; char *p; char sepchar = '\0'; memset(dest, 0, len); p = dest + len - 1; strncpy(dest, fn, len); while(p >= dest) { if(*p == '/' || *p == '\\') { sepchar = *p; found_slash = true; // mark that the path ended with a slash *p = '\0'; break; } *p = '\0'; p--; } // haleyjd: in the case that no slash was ever found, yet the // path string is empty, we are dealing with a file local to the // working directory. The proper path to return for such a string is // not "", but ".", since the format strings add a slash now. When // the string is empty but a slash WAS found, we really do want to // return the empty string, since the path is relative to the root. if(!found_slash && *dest == '\0') *dest = '.'; // if a separator is not found, default to forward, because Windows // supports that too. if(sepchar == '\0') sepchar = '/'; return sepchar; } // // Find out what version of Doom is playing. // void D_IdentifyVersion(void) { // gamemission is set up by the D_FindIWAD function. But if // we specify '-iwad', we have to identify using // IdentifyIWADByName. However, if the iwad does not match // any known IWAD name, we may have a dilemma. Try to // identify by its contents. // STRIFE-TODO: some elaborate checks? for now we assume... // The logic in strife1.exe is simple: // * if strife1.wad is found, set isregistered = true // * if strife0.wad is found, set isdemoversion = true // Make sure gamemode is set up correctly gamemode = commercial; gamemission = strife; isregistered = true; // Load voices.wad if(isregistered) { char *name = D_FindWADByName("voices.wad"); if(!name) // not found? { int p; // haleyjd STRIFE-FIXME: Temporary? // If -iwad was used, check and see if voices.wad exists on the // same filepath. if((p = M_CheckParm("-iwad")) && p < myargc - 1) { char *iwad = myargv[p + 1]; size_t len = strlen(iwad) + 24; char *filename = malloc(len); char sepchar; // how the heck is Choco surviving without this routine? sepchar = M_GetFilePath(iwad, filename, len); filename[strlen(filename)] = sepchar; strcat(filename, "voices.wad"); if(!M_FileExists(filename)) disable_voices = 1; else name = filename; // STRIFE-FIXME: memory leak!! } else disable_voices = 1; } if(disable_voices) // voices disabled? { // STRIFE-FIXME: // if(debugmode) printf("Voices disabled\n"); return; } D_AddFile(name); } } #if 0 // // DoTimeBomb // // haleyjd 08/23/2010: [STRIFE] New function // Code with no xrefs; probably left over from a private alpha or beta. // Translated here because it explains what the SERIAL lump was meant to do. // void DoTimeBomb(void) { dosdate_t date; char *serial; int serialnum; int serial_year; int serial_month; serial = W_CacheLumpName("serial", PU_CACHE); serialnum = atoi(serial); // Rogue, much like Governor Mourel, were lousy liars. These deceptive // error messages are pretty low :P dos_getdate(&date); if(date.year > 1996 || date.day > 15 && date.month > 4) I_Error("Data error! Corrupted WAD File!"); serial_year = serialnum / 10000; serial_month = serialnum / 100 - 100 * serial_year; if(date.year < serial_year || date.day < serialnum - 100 * serial_month - 10000 * serial_year && date.month < serial_month) I_Error("Bad wadfile"); } #endif // Set the gamedescription string void D_SetGameDescription(void) { gamedescription = GetGameName("Strife: Quest for the Sigil"); } static void SetSaveGameDir(char *iwad_filename) { char *sep; char *basefile; // Extract the base filename sep = strrchr(iwad_filename, DIR_SEPARATOR); if (sep == NULL) { basefile = iwad_filename; } else { basefile = sep + 1; } // ~/.chocolate-doom/savegames/ savegamedir = Z_Malloc(strlen(configdir) + 30, PU_STATIC, 0); sprintf(savegamedir, "%ssavegames%c", configdir, DIR_SEPARATOR); M_MakeDirectory(savegamedir); // eg. ~/.chocolate-doom/savegames/doom2.wad/ sprintf(savegamedir + strlen(savegamedir), "%s%c", basefile, DIR_SEPARATOR); M_MakeDirectory(savegamedir); } // Check if the IWAD file is the Chex Quest IWAD. // Returns true if this is chex.wad. // haleyjd 08/23/10: there is no Chex Quest based on Strife, // though I must admit that makes an intriguing idea.... /* static boolean CheckChex(char *iwadname) { char *chex_iwadname = "chex.wad"; return (strlen(iwadname) > strlen(chex_iwadname) && !strcasecmp(iwadname + strlen(iwadname) - strlen(chex_iwadname), chex_iwadname)); } */ // print title for every printed line char title[128]; static boolean D_AddFile(char *filename) { wad_file_t *handle; printf(" adding %s\n", filename); handle = W_AddFile(filename); return handle != NULL; } // Copyright message banners // Some dehacked mods replace these. These are only displayed if they are // replaced by dehacked. // haleyjd 08/22/2010: [STRIFE] altered to match strings from binary static char *copyright_banners[] = { "===========================================================================\n" "ATTENTION: This version of STRIFE has extra files added to it.\n" " You will not receive technical support for modified games.\n" "===========================================================================\n", "===========================================================================\n" " This version is NOT SHAREWARE, do not distribute!\n" " Please report software piracy to the SPA: 1-800-388-PIR8\n" "===========================================================================\n", "===========================================================================\n" " Shareware!\n" "===========================================================================\n" }; // Prints a message only if it has been modified by dehacked. void PrintDehackedBanners(void) { size_t i; for (i=0; i<arrlen(copyright_banners); ++i) { char *deh_s; deh_s = DEH_String(copyright_banners[i]); if (deh_s != copyright_banners[i]) { printf("%s", deh_s); // Make sure the modified banner always ends in a newline character. // If it doesn't, add a newline. This fixes av.wad. if (deh_s[strlen(deh_s) - 1] != '\n') { printf("\n"); } } } } static struct { char *description; char *cmdline; GameVersion_t version; } gameversions[] = { {"Doom 1.9", "1.9", exe_doom_1_9}, {"Ultimate Doom", "ultimate", exe_ultimate}, {"Final Doom", "final", exe_final}, {"Chex Quest", "chex", exe_chex}, { NULL, NULL, 0}, }; // Initialize the game version static void InitGameVersion(void) { int p; int i; //! // @arg <version> // @category compat // // Emulate a specific version of Doom. Valid values are "1.9", // "ultimate" and "final". // p = M_CheckParm("-gameversion"); if (p > 0) { for (i=0; gameversions[i].description != NULL; ++i) { if (!strcmp(myargv[p+1], gameversions[i].cmdline)) { gameversion = gameversions[i].version; break; } } if (gameversions[i].description == NULL) { printf("Supported game versions:\n"); for (i=0; gameversions[i].description != NULL; ++i) { printf("\t%s (%s)\n", gameversions[i].cmdline, gameversions[i].description); } I_Error("Unknown game version '%s'", myargv[p+1]); } } else { // Determine automatically // haleyjd 08/23/10: Removed Chex mode, as it is irrelevant to Strife if (gamemode == shareware || gamemode == registered) { // original gameversion = exe_doom_1_9; } else if (gamemode == retail) { gameversion = exe_ultimate; } else if (gamemode == commercial) { if (gamemission == doom2) { gameversion = exe_doom_1_9; } else { // Final Doom: tnt or plutonia gameversion = exe_final; } } } // The original exe does not support retail - 4th episode not supported if (gameversion < exe_ultimate && gamemode == retail) { gamemode = registered; } // EXEs prior to the Final Doom exes do not support Final Doom. if (gameversion < exe_final && gamemode == commercial) { gamemission = doom2; } } void PrintGameVersion(void) { int i; for (i=0; gameversions[i].description != NULL; ++i) { if (gameversions[i].version == gameversion) { printf("Emulating the behavior of the " "'%s' executable.\n", gameversions[i].description); break; } } } // Load the Chex Quest dehacked file, if we are in Chex mode. // haleyjd 08/23/2010: Removed, as irrelevant to Strife. /* static void LoadChexDeh(void) { char *chex_deh; if (gameversion == exe_chex) { chex_deh = D_FindWADByName("chex.deh"); if (chex_deh == NULL) { I_Error("Unable to find Chex Quest dehacked file (chex.deh).\n" "The dehacked file is required in order to emulate\n" "chex.exe correctly. It can be found in your nearest\n" "/idgames repository mirror at:\n\n" " utils/exe_edit/patches/chexdeh.zip"); } if (!DEH_LoadFile(chex_deh)) { I_Error("Failed to load chex.deh needed for emulating chex.exe."); } } } */ // Function called at exit to display the ENDOOM screen static void D_Endoom(void) { byte *endoom; // Don't show ENDOOM if we have it disabled, or we're running // in screensaver or control test mode. if (!show_endoom || screensaver_mode || M_CheckParm("-testcontrols") > 0) { return; } // haleyjd 08/27/10: [STRIFE] ENDOOM -> ENDSTRF endoom = W_CacheLumpName(DEH_String("ENDSTRF"), PU_STATIC); I_Endoom(endoom); } //============================================================================= // // haleyjd: Chocolate Strife Specifics // // None of the code in here is from the original executable, but is needed for // other reasons. // // D_PatchClipCallback // // haleyjd 08/28/10: Clip patches to the framebuffer without errors. // Returns false if V_DrawPatch should return without drawing. // static boolean D_PatchClipCallback(patch_t *patch, int x, int y) { // note that offsets were already accounted for in V_DrawPatch return (x >= 0 && y >= 0 && x + SHORT(patch->width) <= SCREENWIDTH && y + SHORT(patch->height) <= SCREENHEIGHT); } // // D_InitChocoStrife // // haleyjd 08/28/10: Take care of some Strife-specific initialization // that is necessitated by Chocolate Doom issues, such as setting global // callbacks. // static void D_InitChocoStrife(void) { // set the V_DrawPatch clipping callback V_SetPatchClipCallback(D_PatchClipCallback); } // // End Chocolate Strife Specifics // //============================================================================= // // D_DoomMain // void D_DoomMain (void) { int p; char file[256]; char demolumpname[9]; I_AtExit(D_Endoom, false); M_FindResponseFile (); // print banner I_PrintBanner(PACKAGE_STRING); DEH_printf("Z_Init: Init zone memory allocation daemon. \n"); Z_Init (); #ifdef FEATURE_MULTIPLAYER //! // @category net // // Start a dedicated server, routing packets but not participating // in the game itself. // if (M_CheckParm("-dedicated") > 0) { printf("Dedicated server mode.\n"); NET_DedicatedServer(); // Never returns } //! // @arg <address> // @category net // // Query the status of the server running on the given IP // address. // p = M_CheckParm("-query"); if (p > 0) { NET_QueryAddress(myargv[p+1]); } //! // @category net // // Search the local LAN for running servers. // if (M_CheckParm("-search")) NET_LANQuery(); #endif #ifdef FEATURE_DEHACKED printf("DEH_Init: Init Dehacked support.\n"); DEH_Init(); #endif iwadfile = D_FindIWAD(IWAD_MASK_STRIFE, &gamemission); // None found? if (iwadfile == NULL) { I_Error("Game mode indeterminate. No IWAD file was found. Try\n" "specifying one with the '-iwad' command line parameter.\n"); } modifiedgame = false; //! // @vanilla // // Disable monsters. // nomonsters = M_CheckParm ("-nomonsters"); //! // @vanilla // // Monsters respawn after being killed. // respawnparm = M_CheckParm ("-respawn"); //! // @vanilla // // Monsters move faster. // fastparm = M_CheckParm ("-fast"); //! // @vanilla // // Developer mode. F1 saves a screenshot in the current working // directory. // devparm = M_CheckParm ("-devparm"); I_DisplayFPSDots(devparm); //! // @category net // @vanilla // // Start a deathmatch game. // if (M_CheckParm ("-deathmatch")) deathmatch = 1; //! // @category net // @vanilla // // Start a deathmatch 2.0 game. Weapons do not stay in place and // all items respawn after 30 seconds. // if (M_CheckParm ("-altdeath")) deathmatch = 2; if (devparm) DEH_printf(D_DEVSTR); // find which dir to use for config files #ifdef _WIN32 //! // @platform windows // @vanilla // // Save configuration data and savegames in c:\doomdata, // allowing play from CD. // if (M_CheckParm("-cdrom") > 0) { printf(D_CDROM); // haleyjd 08/22/2010: [STRIFE] Use strife.cd folder for -cdrom M_SetConfigDir("c:\\strife.cd\\"); } else #endif { // Auto-detect the configuration dir. M_SetConfigDir(NULL); } //! // @arg <x> // @vanilla // // Turbo mode. The player's speed is multiplied by x%. If unspecified, // x defaults to 200. Values are rounded up to 10 and down to 400. // if ( (p=M_CheckParm ("-turbo")) ) { int scale = 200; extern int forwardmove[2]; extern int sidemove[2]; if (p<myargc-1) scale = atoi (myargv[p+1]); if (scale < 10) scale = 10; if (scale > 400) scale = 400; DEH_printf("turbo scale: %i%%\n", scale); forwardmove[0] = forwardmove[0]*scale/100; forwardmove[1] = forwardmove[1]*scale/100; sidemove[0] = sidemove[0]*scale/100; sidemove[1] = sidemove[1]*scale/100; } // init subsystems DEH_printf("V_Init: allocate screens.\n"); V_Init (); // Load configuration files before initialising other subsystems. // haleyjd 08/22/2010: [STRIFE] - use strife.cfg DEH_printf("M_LoadDefaults: Load system defaults.\n"); M_SetConfigFilenames("strife.cfg", PROGRAM_PREFIX "strife.cfg"); D_BindVariables(); M_LoadDefaults(); // Save configuration at exit. I_AtExit(M_SaveDefaults, false); DEH_printf("W_Init: Init WADfiles.\n"); D_AddFile(iwadfile); modifiedgame = W_ParseCommandLine(); // add any files specified on the command line with -file wadfile // to the wad list // // convenience hack to allow -wart e m to add a wad file // prepend a tilde to the filename so wadfile will be reloadable p = M_CheckParm ("-wart"); if (p) { myargv[p][4] = 'p'; // big hack, change to -warp // Map name handling. switch (gamemode ) { case shareware: case retail: case registered: sprintf (file,"~"DEVMAPS"E%cM%c.wad", myargv[p+1][0], myargv[p+2][0]); printf("Warping to Episode %s, Map %s.\n", myargv[p+1],myargv[p+2]); break; case commercial: default: p = atoi (myargv[p+1]); if (p<10) sprintf (file,"~"DEVMAPS"cdata/map0%i.wad", p); else sprintf (file,"~"DEVMAPS"cdata/map%i.wad", p); break; } D_AddFile (file); } //! // @arg <demo> // @category demo // @vanilla // // Play back the demo named demo.lmp. // p = M_CheckParm ("-playdemo"); if (!p) { //! // @arg <demo> // @category demo // @vanilla // // Play back the demo named demo.lmp, determining the framerate // of the screen. // p = M_CheckParm ("-timedemo"); } if (p && p < myargc-1) { if (!strcasecmp(myargv[p+1] + strlen(myargv[p+1]) - 4, ".lmp")) { strcpy(file, myargv[p + 1]); } else { sprintf (file,"%s.lmp", myargv[p+1]); } if (D_AddFile (file)) { strncpy(demolumpname, lumpinfo[numlumps - 1].name, 8); demolumpname[8] = '\0'; printf("Playing demo %s.\n", file); } else { // If file failed to load, still continue trying to play // the demo in the same way as Vanilla Doom. This makes // tricks like "-playdemo demo1" possible. strncpy(demolumpname, myargv[p + 1], 8); demolumpname[8] = '\0'; } } I_AtExit((atexit_func_t) G_CheckDemoStatus, true); // Generate the WAD hash table. Speed things up a bit. W_GenerateHashTable(); D_IdentifyVersion(); InitGameVersion(); //LoadChexDeh(); - haleyjd: removed, as irrelevant to Strife D_SetGameDescription(); SetSaveGameDir(iwadfile); // Check for -file in shareware if (modifiedgame) { // These are the lumps that will be checked in IWAD, // if any one is not present, execution will be aborted. // haleyjd 08/22/2010: [STRIFE] Check for Strife lumps. char name[3][8]= { "map23", "map30", "ROB3E1" }; int i; // haleyjd 08/22/2010: [STRIFE] Changed string to match binary // STRIFE-FIXME: Needs to test isdemoversion variable if ( gamemode == shareware) I_Error(DEH_String("\nYou cannot -file with the demo " "version. You must buy the real game!")); // Check for fake IWAD with right name, // but w/o all the lumps of the registered version. // STRIFE-FIXME: Needs to test isregistered variable if (gamemode == registered) for (i = 0; i < 3; i++) if (W_CheckNumForName(name[i])<0) I_Error(DEH_String("\nThis is not the registered version.")); } // get skill / episode / map from parms startskill = sk_medium; startepisode = 1; startmap = 1; autostart = false; //! // @arg <skill> // @vanilla // // Set the game skill, 1-5 (1: easiest, 5: hardest). A skill of // 0 disables all monsters. // p = M_CheckParm ("-skill"); if (p && p < myargc-1) { startskill = myargv[p+1][0]-'1'; autostart = true; } //! // @arg <n> // @vanilla // // Start playing on episode n (1-4) // p = M_CheckParm ("-episode"); if (p && p < myargc-1) { startepisode = myargv[p+1][0]-'0'; startmap = 1; autostart = true; } timelimit = 0; //! // @arg <n> // @category net // @vanilla // // For multiplayer games: exit each level after n minutes. // p = M_CheckParm ("-timer"); if (p && p < myargc-1 && deathmatch) { timelimit = atoi(myargv[p+1]); printf("timer: %i\n", timelimit); } //! // @category net // @vanilla // // Austin Virtual Gaming: end levels after 20 minutes. // p = M_CheckParm ("-avg"); if (p && p < myargc-1 && deathmatch) { DEH_printf("Austin Virtual Gaming: Levels will end " "after 20 minutes\n"); timelimit = 20; } //! // @arg [<x> <y> | <xy>] // @vanilla // // Start a game immediately, warping to ExMy (Doom 1) or MAPxy // (Doom 2) // p = M_CheckParm ("-warp"); if (p && p < myargc-1) { if (gamemode == commercial) startmap = atoi (myargv[p+1]); else { startepisode = myargv[p+1][0]-'0'; if (p + 2 < myargc) { startmap = myargv[p+2][0]-'0'; } else { startmap = 1; } } autostart = true; } // Undocumented: // Invoked by setup to test the controls. p = M_CheckParm("-testcontrols"); if (p > 0) { startepisode = 1; startmap = 1; autostart = true; testcontrols = true; } // Check for load game parameter // We do this here and save the slot number, so that the network code // can override it or send the load slot to other players. //! // @arg <s> // @vanilla // // Load the game in slot s. // p = M_CheckParm ("-loadgame"); if (p && p < myargc-1) { startloadgame = atoi(myargv[p+1]); } else { // Not loading a game startloadgame = -1; } if (W_CheckNumForName("SS_START") >= 0 || W_CheckNumForName("FF_END") >= 0) { I_PrintDivider(); printf(" WARNING: The loaded WAD file contains modified sprites or\n" " floor textures. You may want to use the '-merge' command\n" " line option instead of '-file'.\n"); } I_PrintStartupBanner(gamedescription); PrintDehackedBanners(); // haleyjd 08/28/10: Init Choco Strife stuff. D_InitChocoStrife(); DEH_printf("M_Init: Init miscellaneous info.\n"); M_Init (); // haleyjd 08/22/2010: [STRIFE] Modified string to match binary DEH_printf("R_Init: Loading Graphics - "); R_Init (); DEH_printf("\nP_Init: Init Playloop state.\n"); P_Init (); DEH_printf("I_Init: Setting up machine state.\n"); I_CheckIsScreensaver(); I_InitTimer(); I_InitJoystick(); #ifdef FEATURE_MULTIPLAYER printf ("NET_Init: Init network subsystem.\n"); NET_Init (); #endif DEH_printf("S_Init: Setting up sound.\n"); S_Init (sfxVolume * 8, musicVolume * 8, voiceVolume * 8); // [STRIFE]: voice DEH_printf("D_CheckNetGame: Checking network game status.\n"); D_CheckNetGame (); PrintGameVersion(); DEH_printf("HU_Init: Setting up heads up display.\n"); HU_Init (); DEH_printf("ST_Init: Init status bar.\n"); ST_Init (); // If Doom II without a MAP01 lump, this is a store demo. // Moved this here so that MAP01 isn't constantly looked up // in the main loop. // haleyjd 08/23/2010: [STRIFE] There is no storedemo version of Strife /* if (gamemode == commercial && W_CheckNumForName("map01") < 0) storedemo = true; */ //! // @arg <x> // @category demo // @vanilla // // Record a demo named x.lmp. // p = M_CheckParm ("-record"); if (p && p < myargc-1) { G_RecordDemo (myargv[p+1]); autostart = true; } p = M_CheckParm ("-playdemo"); if (p && p < myargc-1) { singledemo = true; // quit after one demo G_DeferedPlayDemo (demolumpname); D_DoomLoop (); // never returns } p = M_CheckParm ("-timedemo"); if (p && p < myargc-1) { G_TimeDemo (demolumpname); D_DoomLoop (); // never returns } if (startloadgame >= 0) { strcpy(file, P_SaveGameFile(startloadgame)); G_LoadGame (file); } if (gameaction != ga_loadgame ) { if (autostart || netgame) G_InitNew (startskill, startepisode, startmap); else D_StartTitle (); // start up intro loop } D_DoomLoop (); // never returns }