shithub: choc

ref: cb4c334c162601fa2e2675e293e2e4c7a6ae57e4
dir: /src/heretic/d_main.c/

View raw version
// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 1993-2008 Raven Software
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
//-----------------------------------------------------------------------------

// D_main.c

#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 "s_sound.h"

GameMission_t gamemission = heretic;
GameMode_t gamemode = indetermined;
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);

/*
===============================================================================

							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 + 1) & (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 + 1) & (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 (gamemode == 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, &regs, &regs);
    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
        gamemode = shareware;
    }
    else
    {
        gamemode = registered;

        // Is this the extended WAD?
        if (W_CheckNumForName("EXTENDED") != -1)
        {
            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
}